diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/BoxingBenchmark.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/BoxingBenchmark.scala new file mode 100644 index 000000000000..3bc226d605e4 --- /dev/null +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/BoxingBenchmark.scala @@ -0,0 +1,27 @@ +package dotty.tools.benchmarks.inlinetraits + +import org.openjdk.jmh.annotations._ +import java.util.concurrent.TimeUnit.{SECONDS, MILLISECONDS} +import scala.util.Random + +// @BenchmarkMode(Array(Mode.SampleTime)) +@Fork(3) +@Threads(3) +@Warmup(iterations = 5, time = 10, timeUnit = SECONDS) +@Measurement(iterations = 10, time = 10, timeUnit = SECONDS) +// @OutputTimeUnit(MILLISECONDS) +@State(Scope.Benchmark) +class Boxing { + class Wrapper[T](val x: T) + class IntWrapper(val x: Int) + + val numbers = 1 to 1000000 + val ws = numbers.map(Wrapper(_)) + val iws = numbers.map(IntWrapper(_)) + + @Benchmark + def noUnboxing: Int = iws.map(w => IntWrapper(w.x*w.x + 2*w.x)).foldLeft(0)(_ + _.x) + + @Benchmark + def withUnboxing: Int = ws.map(w => Wrapper(w.x*w.x + 2*w.x)).foldLeft(0)(_ + _.x) +} diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/MatrixBenchmark.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/MatrixBenchmark.scala new file mode 100644 index 000000000000..4d34d69fd175 --- /dev/null +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/MatrixBenchmark.scala @@ -0,0 +1,35 @@ +package dotty.tools.benchmarks.inlinetraits + +import org.openjdk.jmh.annotations._ +import java.util.concurrent.TimeUnit.SECONDS +import scala.util.Random + +@Fork(10) +@Threads(3) +@Warmup(iterations = 3, time = 5, timeUnit = SECONDS) +@Measurement(iterations = 5, time = 10, timeUnit = SECONDS) +@State(Scope.Benchmark) +class MatrixBenchmark { + val n: Int = 100 + + def intMatrixElems: List[List[Int]] = + List.tabulate(n, n)((_, _) => Random.nextInt()) + + @Param(Array("standard", "specialized", "inlinetrait")) + var libType: String = _ + + var m1: BenchmarkMatrix = _ + var m2: BenchmarkMatrix = _ + + @Setup(Level.Trial) + def setup = { + Random.setSeed(n) + + val matrixFactory = BenchmarkMatrix.ofType(libType) + m1 = matrixFactory(intMatrixElems) + m2 = matrixFactory(intMatrixElems) + } + + @Benchmark + def matrixBenchmark = (m1 + m2) * m1 // O(n^3) loops +} diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/MatrixOps.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/MatrixOps.scala new file mode 100644 index 000000000000..e316bf05b161 --- /dev/null +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/MatrixOps.scala @@ -0,0 +1,44 @@ +package dotty.tools.benchmarks.inlinetraits + +import standard.IntMatrixLib.{Matrix => StdIntMatrix} +import specialized.IntMatrixLib.{Matrix => SpeIntMatrix} +import inlinetrait.IntMatrixLib.{Matrix => InlIntMatrix} + +trait BenchmarkMatrix: + def +(n: BenchmarkMatrix): BenchmarkMatrix + def *(n: BenchmarkMatrix): BenchmarkMatrix + +object BenchmarkMatrix: + def ofType(tpe: String): Seq[Seq[Int]] => BenchmarkMatrix = + (elems: Seq[Seq[Int]]) => tpe.toLowerCase() match { + case "standard" => StdBenchmarkMatrix(StdIntMatrix(elems*)) + case "specialized" => SpeBenchmarkMatrix(SpeIntMatrix(elems*)) + case "inlinetrait" => InlBenchmarkMatrix(InlIntMatrix(elems*)) + } + +private class StdBenchmarkMatrix(val m: StdIntMatrix) extends BenchmarkMatrix: + import standard.IntMatrixLib.{+, `*`} + override def +(n: BenchmarkMatrix): StdBenchmarkMatrix = n match { + case stdN: StdBenchmarkMatrix => StdBenchmarkMatrix(this.m + stdN.m) + } + override def *(n: BenchmarkMatrix): StdBenchmarkMatrix = n match { + case stdN: StdBenchmarkMatrix => StdBenchmarkMatrix(this.m * stdN.m) + } + +private class SpeBenchmarkMatrix(val m: SpeIntMatrix) extends BenchmarkMatrix: + import specialized.IntMatrixLib.{+ => plus, `*` => times} + override def +(n: BenchmarkMatrix): SpeBenchmarkMatrix = n match { + case speN: SpeBenchmarkMatrix => SpeBenchmarkMatrix(plus(this.m)(speN.m)) + } + override def *(n: BenchmarkMatrix): SpeBenchmarkMatrix = n match { + case speN: SpeBenchmarkMatrix => SpeBenchmarkMatrix(times(this.m)(speN.m)) + } + +private class InlBenchmarkMatrix(val m: InlIntMatrix) extends BenchmarkMatrix: + import inlinetrait.IntMatrixLib.{+, `*`} + override def +(n: BenchmarkMatrix): InlBenchmarkMatrix = n match { + case inlN: InlBenchmarkMatrix => InlBenchmarkMatrix(this.m + inlN.m) + } + override def *(n: BenchmarkMatrix): InlBenchmarkMatrix = n match { + case inlN: InlBenchmarkMatrix => InlBenchmarkMatrix(this.m * inlN.m) + } \ No newline at end of file diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/PairsBenchmark.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/PairsBenchmark.scala new file mode 100644 index 000000000000..197b7cf714ab --- /dev/null +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/PairsBenchmark.scala @@ -0,0 +1,39 @@ +package dotty.tools.benchmarks.inlinetraits + +import org.openjdk.jmh.annotations._ +import java.util.concurrent.TimeUnit.SECONDS +import scala.util.Random + +@Fork(10) +@Threads(3) +@Warmup(iterations = 3, time = 5, timeUnit = SECONDS) +@Measurement(iterations = 5, time = 10, timeUnit = SECONDS) +@State(Scope.Benchmark) +class PairsBenchmark { + val numPairs: Int = 3_000_000 + + def pairElems: List[(First, Second)] = List.tabulate(numPairs)(_ % 2 match { + case 0 => (Random.nextInt(), Random.nextDouble()) + case 1 => (Random.nextInt(Char.MaxValue).asInstanceOf[Char], Random.nextInt(Short.MaxValue).asInstanceOf[Short]) + }) + + @Param(Array("standard", "specialized", "inlinetrait")) + var libType: String = _ + + var pairs: List[BenchmarkPair] = _ + + @Setup(Level.Trial) + def setup = { + Random.setSeed(numPairs) + + val pairFactory = (l: List[(First, Second)]) => l.map((_1, _2) => BenchmarkPair.ofType(libType)(_1, _2)) + pairs = pairFactory(pairElems) + } + + @Benchmark + def pairsBenchmark = pairs.foldLeft(0){ case (sum, pair) => pair match { + case BenchmarkPair(i: Int, d: Double) => 7 * i + 3 * d.toInt + sum + case BenchmarkPair(c: Char, s: Short) => 5 * c + 2 * s + sum + } + } +} diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/PairsOps.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/PairsOps.scala new file mode 100644 index 000000000000..90c74d7da17c --- /dev/null +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/PairsOps.scala @@ -0,0 +1,39 @@ +package dotty.tools.benchmarks.inlinetraits + +import standard.{Pair => StdPair} +import specialized.{Pair => SpePair} +import inlinetrait.{ + Pair => InlPair, + IntDoublePair => IDPair, + CharShortPair => CSPair, +} + +type First = Int | Char +type Second = Double | Short + +class BenchmarkPair private (val _1: First, val _2: Second): + def this(p: StdPair[First, Second]) = this(p._1, p._2) + def this(p: SpePair[First, Second]) = this(p._1, p._2) + def this(p: InlPair[First, Second]) = this(p._1, p._2) + +object BenchmarkPair: + def ofType(tpe: String): (First, Second) => BenchmarkPair = + (_1: First, _2: Second) => tpe.toLowerCase() match { + case "standard" => BenchmarkPair(StdPair(_1, _2)) + case "specialized" => + val concretePair: SpePair[First, Second] = (_1, _2) match { + case (_1: Int, _2: Double) => SpePair(_1, _2) + case (_1: Char, _2: Short) => SpePair(_1, _2) + case _ => ??? + } + BenchmarkPair(concretePair) + case "inlinetrait" => + val concretePair: InlPair[First, Second] = (_1, _2) match { + case (_1: Int, _2: Double) => IDPair(_1, _2) + case (_1: Char, _2: Short) => CSPair(_1, _2) + case _ => ??? + } + BenchmarkPair(concretePair) + } + + def unapply(p: BenchmarkPair): Option[(First, Second)] = Some(p._1, p._2) diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/inlinetrait/Matrix.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/inlinetrait/Matrix.scala new file mode 100644 index 000000000000..9653c65d2bac --- /dev/null +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/inlinetrait/Matrix.scala @@ -0,0 +1,47 @@ +package dotty.tools.benchmarks.inlinetraits +package inlinetrait + +import scala.reflect.ClassTag + +inline trait MatrixLib[T: ClassTag]: + // FIXME make Matrix opaque once inlinedTypeRef works properly + /*opaque*/ type Matrix = Array[Array[T]] + + // FIXME uncomment object and remove following code when inner objects work + /* + * object Matrix: + * def apply(rows: Seq[T]*): Matrix = + * rows.map(_.toArray).toArray + */ + // ---------------------------------- + def Matrix(rows: Seq[T]*): Matrix = + rows.map(_.toArray).toArray + // ---------------------------------- + // end of code to remove + + extension (m: Matrix) + def apply(x: Int)(y: Int): T = m(x)(y) + def rows: Int = m.length + def cols: Int = m(0).length + +object IntMatrixLib extends MatrixLib[Int]: + extension (m: Matrix) + def +(n: Matrix): Matrix = + val sum = + for row <- 0 until m.rows + yield + for col <- 0 until m.cols + yield m(row)(col) + n(row)(col) + Matrix(sum*) + end + + + def *(n: Matrix): Matrix = + val prod = + for i <- 0 until m.rows + yield + for j <- 0 until n.cols + yield + val mults = for k <- 0 until n.rows yield m(i)(k) * n(k)(j) + mults.fold(0)(_ + _) + Matrix(prod*) + end * \ No newline at end of file diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/inlinetrait/Pair.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/inlinetrait/Pair.scala new file mode 100644 index 000000000000..996e38fb293f --- /dev/null +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/inlinetrait/Pair.scala @@ -0,0 +1,7 @@ +package dotty.tools.benchmarks.inlinetraits +package inlinetrait + +inline trait Pair[+T1, +T2](val _1: T1, val _2: T2) + +class IntDoublePair(override val _1: Int, override val _2: Double) extends Pair[Int, Double](_1, _2) +class CharShortPair(override val _1: Char, override val _2: Short) extends Pair[Char, Short](_1, _2) \ No newline at end of file diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/specialized/Matrix.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/specialized/Matrix.scala new file mode 100644 index 000000000000..6043da8207be --- /dev/null +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/specialized/Matrix.scala @@ -0,0 +1,113 @@ +package dotty.tools.benchmarks.inlinetraits +package specialized + +/* + * This implementation relies on the @specialized tag to specialize the MatrixLib. + * However, @specialized does nothing in Scala 3. Therefore, an equivalent version is + * recreated by hand further down, and the actual Scala 2 code is provided below: + * + * import scala.reflect.ClassTag + * import scala.specialized + * + * class MatrixLib[@specialized(Int) T: ClassTag] { + * type Matrix = Array[Array[T]] + * + * object Matrix { + * def apply(rows: Seq[T]*): Matrix = + * rows.map(_.toArray).toArray + * } + * + * def get(m: Matrix)(x: Int)(y: Int): T = m(x)(y) + * def rows(m: Matrix): Int = m.length + * def cols(m: Matrix): Int = m(0).length + * } + * + * object IntMatrixLib extends MatrixLib[Int] { + * def +(m: Matrix)(n: Matrix): Matrix = { + * val sum = { + * for (row <- 0 until rows(m)) + * yield { + * for (col <- 0 until cols(m)) + * yield m(row)(col) + n(row)(col) + * } + * } + * Matrix(sum: _*) + * } + * + * def *(m: Matrix)(n: Matrix): Matrix = { + * val prod = { + * for (i <- 0 until rows(m)) + * yield { + * for (j <- 0 until cols(n)) + * yield { + * val mults = + * for (k <- 0 until rows(n)) yield get(m)(i)(k) * get(n)(k)(j) + * mults.fold(0)(_ + _) + * } + * } + * } + * Matrix(prod: _*) + * } + * } + */ + +import scala.reflect.ClassTag +import scala.specialized + +class MatrixLib { + private[specialized] type ObjectMatrix = Array[Array[Object]] + private[specialized] type IntMatrix = Array[Array[Int]] + + def get(m: ObjectMatrix)(x: Int)(y: Int): Object = m(x)(y) + def rows(m: ObjectMatrix): Int = m.length + def cols(m: ObjectMatrix): Int = m(0).length + private[specialized] def get$mcI$sp(m: IntMatrix)(x: Int)(y: Int): Int = Int.unbox(get(m.asInstanceOf[ObjectMatrix])(x)(y)) + private[specialized] def rows$mcI$sp(m: IntMatrix): Int = rows(m.asInstanceOf[ObjectMatrix]) + private[specialized] def cols$mcI$sp(m: IntMatrix): Int = cols(m.asInstanceOf[ObjectMatrix]) +} + +private class MatrixLib$mcI$sp extends MatrixLib { + override def get(m: ObjectMatrix)(x: Int)(y: Int): Object = Int.box(get(m.asInstanceOf[IntMatrix])(x)(y)) + override def rows(m: ObjectMatrix): Int = rows(m.asInstanceOf[IntMatrix]) + override def cols(m: ObjectMatrix): Int = cols(m.asInstanceOf[IntMatrix]) + def get(m: IntMatrix)(x: Int)(y: Int): Int = get$mcI$sp(m)(x)(y) + def rows(m: IntMatrix): Int = rows$mcI$sp(m) + def cols(m: IntMatrix): Int = cols$mcI$sp(m) + override private[specialized] def get$mcI$sp(m: IntMatrix)(x: Int)(y: Int): Int = m(x)(y) + override private[specialized] def rows$mcI$sp(m: IntMatrix): Int = m.length + override private[specialized] def cols$mcI$sp(m: IntMatrix): Int = m(0).length +} + +object IntMatrixLib extends MatrixLib$mcI$sp { + type Matrix = IntMatrix + object Matrix { + def apply(rows: Seq[Int]*): IntMatrix = + rows.map(_.toArray).toArray + } + + def +(m: Matrix)(n: Matrix): Matrix = { + val sum = { + for (row <- 0 until rows(m)) + yield { + for (col <- 0 until cols(m)) + yield m(row)(col) + n(row)(col) + } + } + Matrix(sum: _*) + } + + def *(m: Matrix)(n: Matrix): Matrix = { + val prod = { + for (i <- 0 until rows(m)) + yield { + for (j <- 0 until cols(n)) + yield { + val mults = + for (k <- 0 until rows(n)) yield get(m)(i)(k) * get(n)(k)(j) + mults.fold(0)(_ + _) + } + } + } + Matrix(prod: _*) + } +} \ No newline at end of file diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/specialized/Pair.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/specialized/Pair.scala new file mode 100644 index 000000000000..2eb3fc149180 --- /dev/null +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/specialized/Pair.scala @@ -0,0 +1,26 @@ +package dotty.tools.benchmarks.inlinetraits +package specialized + +/* + * This implementation relies on the @specialized tag to specialize Pair. + * However, @specialized does nothing in Scala 3. Therefore, an equivalent version is + * recreated by hand further down, and the actual Scala 2 code is provided below: + * + * import scala.specialized + * + * class Pair[@specialized(Int, Char) +T1, @specialized(Double, Short) +T2](_1: T1, _2: T2) {} + */ + +class Pair[+T1, +T2] protected (val _1: T1, val _2: T2) + +object Pair { + def apply(_1: Int, _2: Double): Pair[Int, Double] = new Pair$mcID$sp(_1, _2) + def apply(_1: Int, _2: Short): Pair[Int, Short] = new Pair$mcIS$sp(_1, _2) + def apply(_1: Char, _2: Double): Pair[Char, Double] = new Pair$mcCD$sp(_1, _2) + def apply(_1: Char, _2: Short): Pair[Char, Short] = new Pair$mcCS$sp(_1, _2) +} + +private class Pair$mcID$sp(protected[this] val _1$mcI$sp: Int, protected[this] val _2$mcD$sp: Double) extends Pair[Int, Double](_1$mcI$sp, _2$mcD$sp) +private class Pair$mcIS$sp(protected[this] val _1$mcI$sp: Int, protected[this] val _2$mcS$sp: Short) extends Pair[Int, Short](_1$mcI$sp, _2$mcS$sp) +private class Pair$mcCD$sp(protected[this] val _1$mcC$sp: Char, protected[this] val _2$mcD$sp: Double) extends Pair[Char, Double](_1$mcC$sp, _2$mcD$sp) +private class Pair$mcCS$sp(protected[this] val _1$mcC$sp: Char, protected[this] val _2$mcS$sp: Short) extends Pair[Char, Short](_1$mcC$sp, _2$mcS$sp) diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/standard/Matrix.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/standard/Matrix.scala new file mode 100644 index 000000000000..66c124e0086d --- /dev/null +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/standard/Matrix.scala @@ -0,0 +1,38 @@ +package dotty.tools.benchmarks.inlinetraits +package standard + +import scala.reflect.ClassTag + +trait MatrixLib[T: ClassTag]: + opaque type Matrix = Array[Array[T]] + + object Matrix: + def apply(rows: Seq[T]*): Matrix = + rows.map(_.toArray).toArray + + extension (m: Matrix) + def apply(x: Int)(y: Int): T = m(x)(y) + def rows: Int = m.length + def cols: Int = m(0).length + +object IntMatrixLib extends MatrixLib[Int]: + extension (m: Matrix) + def +(n: Matrix): Matrix = + val sum = + for row <- 0 until m.rows + yield + for col <- 0 until m.cols + yield m(row)(col) + n(row)(col) + Matrix(sum*) + end + + + def *(n: Matrix): Matrix = + val prod = + for i <- 0 until m.rows + yield + for j <- 0 until n.cols + yield + val mults = for k <- 0 until n.rows yield m(i)(k) * n(k)(j) + mults.fold(0)(_ + _) + Matrix(prod*) + end * \ No newline at end of file diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/standard/Numeric.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/standard/Numeric.scala new file mode 100644 index 000000000000..21325c2ca85a --- /dev/null +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/standard/Numeric.scala @@ -0,0 +1,26 @@ +package dotty.tools.benchmarks.inlinetraits +package standard + +trait Numeric[T] { + def zero: T + def plus(x: T, y: T): T + def times(x: T, y: T): T +} + +object Numeric { + @inline def apply[T](implicit num: Numeric[T]): Numeric[T] = num + + trait IntIsNumeric extends Numeric[Int] { + def zero: Int = 0 + def plus(x: Int, y: Int): Int = x + y + def times(x: Int, y: Int): Int = x * y + } + implicit object IntIsNumeric extends IntIsNumeric + + trait DoubleIsNumeric extends Numeric[Double] { + def zero: Double = 0d + def plus(x: Double, y: Double): Double = x + y + def times(x: Double, y: Double): Double = x * y + } + implicit object DoubleIsNumeric extends DoubleIsNumeric +} diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/standard/Pair.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/standard/Pair.scala new file mode 100644 index 000000000000..b5cd0a684cb4 --- /dev/null +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/standard/Pair.scala @@ -0,0 +1,4 @@ +package dotty.tools.benchmarks.inlinetraits +package standard + +class Pair[+T1, +T2](val _1: T1, val _2: T2) diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index a6118732d4ae..e25061883d72 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -48,6 +48,7 @@ class Compiler { /** Phases dealing with TASTY tree pickling and unpickling */ protected def picklerPhases: List[List[Phase]] = List(new Pickler) :: // Generate TASTY info + List(new SpecializeInlineTraits) :: // Inline the code of inline traits into their children List(new Inlining) :: // Inline and execute macros List(new PostInlining) :: // Add mirror support for inlined code List(new CheckUnused.PostInlining) :: // Check for unused elements @@ -91,7 +92,8 @@ class Compiler { new ExplicitSelf, // Make references to non-trivial self types explicit as casts new StringInterpolatorOpt, // Optimizes raw and s and f string interpolators by rewriting them to string concatenations or formats new DropBreaks) :: // Optimize local Break throws by rewriting them - List(new PruneErasedDefs, // Drop erased definitions from scopes and simplify erased expressions + List(new PruneInlineTraits, // Remove right-hand side of definitions in inline traits + new PruneErasedDefs, // Drop erased definitions from scopes and simplify erased expressions new UninitializedDefs, // Replaces `compiletime.uninitialized` by `_` new InlinePatterns, // Remove placeholders of inlined patterns new VCInlineMethods, // Inlines calls to value class methods diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index 8100bea374eb..05ba42c12c97 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -446,13 +446,13 @@ object Flags { /** Flags representing source modifiers */ private val CommonSourceModifierFlags: FlagSet = - commonFlags(Private, Protected, Final, Case, Implicit, Given, Override, JavaStatic, Transparent, Erased) + commonFlags(Private, Protected, Final, Case, Implicit, Given, Override, JavaStatic, Transparent, Erased, Inline) val TypeSourceModifierFlags: FlagSet = CommonSourceModifierFlags.toTypeFlags | Abstract | Sealed | Opaque | Open val TermSourceModifierFlags: FlagSet = - CommonSourceModifierFlags.toTermFlags | Inline | AbsOverride | Lazy + CommonSourceModifierFlags.toTermFlags | AbsOverride | Lazy /** Flags representing modifiers that can appear in trees */ val ModifierFlags: FlagSet = @@ -574,6 +574,7 @@ object Flags { val LazyGiven: FlagSet = Given | Lazy val InlineOrProxy: FlagSet = Inline | InlineProxy // An inline method or inline argument proxy */ val InlineMethod: FlagSet = Inline | Method + val InlineTrait: FlagSet = Inline | Trait val InlineParam: FlagSet = Inline | Param val InlineByNameProxy: FlagSet = InlineProxy | Method val JavaEnumTrait: FlagSet = JavaDefined | Enum // A Java enum trait diff --git a/compiler/src/dotty/tools/dotc/core/Phases.scala b/compiler/src/dotty/tools/dotc/core/Phases.scala index 3c4c45ab254a..ec01f6413a07 100644 --- a/compiler/src/dotty/tools/dotc/core/Phases.scala +++ b/compiler/src/dotty/tools/dotc/core/Phases.scala @@ -210,6 +210,7 @@ object Phases { private var myPostTyperPhase: Phase = _ private var mySbtExtractDependenciesPhase: Phase = _ private var myPicklerPhase: Phase = _ + private var mySpecializeInlineTraitsPhase: Phase = _ private var myInliningPhase: Phase = _ private var myStagingPhase: Phase = _ private var mySplicingPhase: Phase = _ @@ -235,6 +236,7 @@ object Phases { final def postTyperPhase: Phase = myPostTyperPhase final def sbtExtractDependenciesPhase: Phase = mySbtExtractDependenciesPhase final def picklerPhase: Phase = myPicklerPhase + final def specializeInlineTraitsPhase: Phase = mySpecializeInlineTraitsPhase final def inliningPhase: Phase = myInliningPhase final def stagingPhase: Phase = myStagingPhase final def splicingPhase: Phase = mySplicingPhase @@ -263,6 +265,7 @@ object Phases { myPostTyperPhase = phaseOfClass(classOf[PostTyper]) mySbtExtractDependenciesPhase = phaseOfClass(classOf[sbt.ExtractDependencies]) myPicklerPhase = phaseOfClass(classOf[Pickler]) + mySpecializeInlineTraitsPhase = phaseOfClass(classOf[SpecializeInlineTraits]) myInliningPhase = phaseOfClass(classOf[Inlining]) myStagingPhase = phaseOfClass(classOf[Staging]) mySplicingPhase = phaseOfClass(classOf[Splicing]) @@ -451,8 +454,9 @@ object Phases { def postTyperPhase(using Context): Phase = ctx.base.postTyperPhase def sbtExtractDependenciesPhase(using Context): Phase = ctx.base.sbtExtractDependenciesPhase def picklerPhase(using Context): Phase = ctx.base.picklerPhase + def specializeInlineTraitsPhase(using Context): Phase = ctx.base.specializeInlineTraitsPhase def inliningPhase(using Context): Phase = ctx.base.inliningPhase - def stagingPhase(using Context): Phase = ctx.base.stagingPhase + def stagingPhase(using Context): Phase = ctx.base.stagingPhase def splicingPhase(using Context): Phase = ctx.base.splicingPhase def firstTransformPhase(using Context): Phase = ctx.base.firstTransformPhase def refchecksPhase(using Context): Phase = ctx.base.refchecksPhase diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index cd9526b27a21..d13c821c662a 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -26,6 +26,7 @@ object StdNames { inline val LOCALDUMMY_PREFIX = " + val ctx1 = localContext(cls)(using ctx0).addMode(Mode.ReadPositions) + inContext(sourceChangeContext(Addr(0))(using ctx1)) { + // avoids space leaks by not capturing the current context + + val fork = forkAt(statsStart) + val stats = fork.readIndexedStats(localDummy, end) + val inlinedMembers = (tparams ++ vparams ++ stats).filter(member => Inlines.isInlineableFromInlineTrait(cls, member)) + Block(inlinedMembers, unitLiteral).withSpan(cls.span) + } + }) defn.patchStdLibClass(cls) NamerOps.addConstructorProxies(cls) setSpan(start, diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index 73fa2a2871a2..447eecbca36f 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -115,9 +115,10 @@ object Inliner: oldOwners: List[Symbol], newOwners: List[Symbol], substFrom: List[Symbol], - substTo: List[Symbol])(using Context) + substTo: List[Symbol], + val inlineCopier: TreeCopier)(using Context) extends TreeTypeMap( - typeMap, treeMap, oldOwners, newOwners, substFrom, substTo, InlineCopier()): + typeMap, treeMap, oldOwners, newOwners, substFrom, substTo, inlineCopier): override def copy( typeMap: Type => Type, @@ -126,7 +127,7 @@ object Inliner: newOwners: List[Symbol], substFrom: List[Symbol], substTo: List[Symbol])(using Context) = - new InlinerMap(typeMap, treeMap, oldOwners, newOwners, substFrom, substTo) + new InlinerMap(typeMap, treeMap, oldOwners, newOwners, substFrom, substTo, inlineCopier) override def transformInlined(tree: Inlined)(using Context) = if tree.call.isEmpty then @@ -283,7 +284,7 @@ class Inliner(val call: tpd.Tree)(using Context): private def classNestingLevel(cls: Symbol) = cls.ownersIterator.count(_.isClass) // Compute val-definitions for all this-proxies and append them to `bindingsBuf` - private def computeThisBindings() = { + protected def computeThisBindings() = { // All needed this-proxies, paired-with and sorted-by nesting depth of // the classes they represent (innermost first) val sortedProxies = thisProxy.toList @@ -430,7 +431,7 @@ class Inliner(val call: tpd.Tree)(using Context): else arg else arg - private def canElideThis(tpe: ThisType): Boolean = + protected def canElideThis(tpe: ThisType): Boolean = inlineCallPrefix.tpe == tpe && ctx.owner.isContainedIn(tpe.cls) || tpe.cls.isContainedIn(inlinedMethod) || tpe.cls.is(Package) @@ -493,7 +494,7 @@ class Inliner(val call: tpd.Tree)(using Context): * from its `originalOwner`, and, if it comes from outside the inlined method * itself, it has to be marked as an inlined argument. */ - private def integrate(tree: Tree, originalOwner: Symbol)(using Context): Tree = + protected def integrate(tree: Tree, originalOwner: Symbol)(using Context): Tree = // assertAllPositioned(tree) // debug tree.changeOwner(originalOwner, ctx.owner) @@ -505,9 +506,73 @@ class Inliner(val call: tpd.Tree)(using Context): val reducer = new InlineReducer(this) - /** The Inlined node representing the inlined call */ - def inlined(rhsToInline: tpd.Tree): (List[MemberDef], Tree) = + protected class InlinerTypeMap extends DeepTypeMap { + override def stopAt = + if opaqueProxies.isEmpty then StopAt.Static else StopAt.Package + def apply(t: Type) = t match { + case t: ThisType => thisProxy.get(t.cls).getOrElse(t) + case t: TypeRef => paramProxy.getOrElse(t, mapOver(t)) + case t: SingletonType => + if t.termSymbol.isAllOf(InlineParam) then apply(t.widenTermRefExpr) + else paramProxy.getOrElse(t, mapOver(t)) + case t => mapOver(t) + } + } + + protected class InlinerTreeMap extends (Tree => Tree) { + def apply(tree: Tree) = tree match { + case tree: This => + tree.tpe match { + case thistpe: ThisType => + thisProxy.get(thistpe.cls) match { + case Some(t) => + val thisRef = ref(t).withSpan(call.span) + inlinedFromOutside(thisRef)(tree.span) + case None => tree + } + case _ => tree + } + case tree: Ident => + /* Span of the argument. Used when the argument is inlined directly without a binding */ + def argSpan = + if (tree.name == nme.WILDCARD) tree.span // From type match + else if (tree.symbol.isTypeParam && tree.symbol.owner.isClass) tree.span // TODO is this the correct span? + else paramSpan(tree.name) + val inlinedCtx = ctx.withSource(inlinedMethod.topLevelClass.source) + paramProxy.get(tree.tpe) match { + case Some(t) if tree.isTerm && t.isSingleton => + val inlinedSingleton = singleton(t).withSpan(argSpan) + inlinedFromOutside(inlinedSingleton)(tree.span) + case Some(t) if tree.isType => + inlinedFromOutside(TypeTree(t).withSpan(argSpan))(tree.span) + case _ => tree + } + case tree @ Select(qual: This, name) if tree.symbol.is(Private) && tree.symbol.isInlineMethod => + // This inline method refers to another (private) inline method (see tests/pos/i14042.scala). + // We insert upcast to access the private inline method once inlined. This makes the selection + // keep the symbol when re-typechecking in the InlineTyper. The method is inlined and hence no + // reference to a private method is kept at runtime. + cpy.Select(tree)(qual.asInstance(qual.tpe.widen), name) + + case tree => tree + } + + private def inlinedFromOutside(tree: Tree)(span: Span): Tree = + Inlined(EmptyTree, Nil, tree)(using ctx.withSource(inlinedMethod.topLevelClass.source)).withSpan(span) + } + protected val inlinerTypeMap: InlinerTypeMap = InlinerTypeMap() + protected val inlinerTreeMap: InlinerTreeMap = InlinerTreeMap() + + protected def substFrom: List[Symbol] = Nil + protected def substTo: List[Symbol] = Nil + protected def inlineCopier: TreeCopier = InlineCopier() + + protected def inlineCtx(inlineTyper: InlineTyper)(using Context): Context = + inlineContext(call).fresh.setTyper(inlineTyper).setNewScope + + /** The Inlined node representing the inlined call */ + def inlined(rhsToInline: tpd.Tree)(using Context): (List[MemberDef], Tree) = inlining.println(i"-----------------------\nInlining $call\nWith RHS $rhsToInline") def paramTypess(call: Tree, acc: List[List[Type]]): List[List[Type]] = call match @@ -549,69 +614,20 @@ class Inliner(val call: tpd.Tree)(using Context): val inlineTyper = new InlineTyper(ctx.reporter.errorCount) - val inlineCtx = inlineContext(call).fresh.setTyper(inlineTyper).setNewScope - - def inlinedFromOutside(tree: Tree)(span: Span): Tree = - Inlined(EmptyTree, Nil, tree)(using ctx.withSource(inlinedMethod.topLevelClass.source)).withSpan(span) + val inlineCtx = this.inlineCtx(inlineTyper) // A tree type map to prepare the inlined body for typechecked. // The translation maps references to `this` and parameters to // corresponding arguments or proxies on the type and term level. It also changes // the owner from the inlined method to the current owner. val inliner = new InlinerMap( - typeMap = - new DeepTypeMap { - override def stopAt = - if opaqueProxies.isEmpty then StopAt.Static else StopAt.Package - def apply(t: Type) = t match { - case t: ThisType => thisProxy.getOrElse(t.cls, t) - case t: TypeRef => paramProxy.getOrElse(t, mapOver(t)) - case t: SingletonType => - if t.termSymbol.isAllOf(InlineParam) then apply(t.widenTermRefExpr) - else paramProxy.getOrElse(t, mapOver(t)) - case t => mapOver(t) - } - }, - treeMap = { - case tree: This => - tree.tpe match { - case thistpe: ThisType => - thisProxy.get(thistpe.cls) match { - case Some(t) => - val thisRef = ref(t).withSpan(call.span) - inlinedFromOutside(thisRef)(tree.span) - case None => tree - } - case _ => tree - } - case tree: Ident => - /* Span of the argument. Used when the argument is inlined directly without a binding */ - def argSpan = - if (tree.name == nme.WILDCARD) tree.span // From type match - else if (tree.symbol.isTypeParam && tree.symbol.owner.isClass) tree.span // TODO is this the correct span? - else paramSpan(tree.name) - val inlinedCtx = ctx.withSource(inlinedMethod.topLevelClass.source) - paramProxy.get(tree.tpe) match { - case Some(t) if tree.isTerm && t.isSingleton => - val inlinedSingleton = singleton(t).withSpan(argSpan) - inlinedFromOutside(inlinedSingleton)(tree.span) - case Some(t) if tree.isType => - inlinedFromOutside(TypeTree(t).withSpan(argSpan))(tree.span) - case _ => tree - } - case tree @ Select(qual: This, name) if tree.symbol.is(Private) && tree.symbol.isInlineMethod => - // This inline method refers to another (private) inline method (see tests/pos/i14042.scala). - // We insert upcast to access the private inline method once inlined. This makes the selection - // keep the symbol when re-typechecking in the InlineTyper. The method is inlined and hence no - // reference to a private method is kept at runtime. - cpy.Select(tree)(qual.asInstance(qual.tpe.widen), name) - - case tree => tree - }, + typeMap = inlinerTypeMap, + treeMap = inlinerTreeMap, oldOwners = inlinedMethod :: Nil, newOwners = ctx.owner :: Nil, - substFrom = Nil, - substTo = Nil + substFrom = substFrom, + substTo = substTo, + inlineCopier = inlineCopier )(using inlineCtx) inlining.println( diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index bcc10ffa6db8..70b352eb31e8 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -3,8 +3,9 @@ package dotc package inlines import ast.*, core.* -import Flags.*, Symbols.*, Types.*, Decorators.*, Constants.*, Contexts.* -import StdNames.tpnme +import Flags.*, Symbols.*, Types.*, Decorators.*, Constants.*, Contexts.*, TypeOps.* +import Names.Name +import StdNames.{str, nme, tpnme} import transform.SymUtils._ import typer.* import NameKinds.BodyRetainerName @@ -18,7 +19,8 @@ import staging.StagingLevel import collection.mutable import reporting.{NotConstant, trace} -import util.Spans.Span +import util.Spans.{Span, spanCoord} +import NameOps.expandedName /** Support for querying inlineable methods and for inlining calls to such methods */ object Inlines: @@ -32,7 +34,7 @@ object Inlines: /** `sym` is an inline method with a known body to inline. */ def hasBodyToInline(sym: SymDenotation)(using Context): Boolean = - sym.isInlineMethod && sym.hasAnnotation(defn.BodyAnnot) + (sym.isInlineMethod || sym.isInlineTrait) && sym.hasAnnotation(defn.BodyAnnot) /** The body to inline for method `sym`, or `EmptyTree` if none exists. * @pre hasBodyToInline(sym) @@ -43,27 +45,69 @@ object Inlines: else EmptyTree + def defsToInline(traitSym: SymDenotation)(using Context): List[Tree] = + bodyToInline(traitSym) match + case Block(defs, _) if traitSym.isInlineTrait => defs + case _ => Nil + /** Are we in an inline method body? */ def inInlineMethod(using Context): Boolean = ctx.owner.ownersIterator.exists(_.isInlineMethod) + def inInlineContext(using Context): Boolean = + ctx.owner.ownersIterator.exists(sym => sym.isInlineMethod || sym.isInlineTrait) + /** Can a call to method `meth` be inlined? */ def isInlineable(meth: Symbol)(using Context): Boolean = - meth.is(Inline) && meth.hasAnnotation(defn.BodyAnnot) && !inInlineMethod + meth.isInlineMethod && meth.hasAnnotation(defn.BodyAnnot) && !inInlineMethod + + def isInlineableFromInlineTrait(inlinedTraitSym: ClassSymbol, member: tpd.Tree)(using Context): Boolean = + !(member.isInstanceOf[tpd.TypeDef] && inlinedTraitSym.typeParams.contains(member.symbol)) + && !member.symbol.isAllOf(Inline) + && !member.symbol.is(Deferred) /** Should call be inlined in this context? */ - def needsInlining(tree: Tree)(using Context): Boolean = tree match { - case Block(_, expr) => needsInlining(expr) - case _ => - isInlineable(tree.symbol) - && !tree.tpe.widenTermRefExpr.isInstanceOf[MethodOrPoly] - && StagingLevel.level == 0 + def needsInlining(tree: Tree)(using Context): Boolean = + def isInlineableInCtx = + StagingLevel.level == 0 && ( ctx.phase == Phases.inliningPhase || (ctx.phase == Phases.typerPhase && needsTransparentInlining(tree)) + || (ctx.phase == Phases.specializeInlineTraitsPhase && !tree.symbol.is(Macro)) ) && !ctx.typer.hasInliningErrors && !ctx.base.stopInlining + && !ctx.owner.ownersIterator.exists(_.isInlineTrait) + + tree match + case Block(_, expr) => + needsInlining(expr) + case tdef @ TypeDef(_, impl: Template) => + impl.parents.map(symbolFromParent).exists(_.isInlineTrait) && isInlineableInCtx + case _ => + isInlineable(tree.symbol) && !tree.tpe.widenTermRefExpr.isInstanceOf[MethodOrPoly] && isInlineableInCtx + + private[dotc] def symbolFromParent(parent: Tree)(using Context): Symbol = + if parent.symbol.isConstructor then parent.symbol.owner else parent.symbol + + private def inlineTraitAncestors(cls: TypeDef)(using Context): List[Tree] = cls match { + case tpd.TypeDef(_, tmpl: Template) => + val parentTrees: Map[Symbol, Tree] = tmpl.parents.map(par => symbolFromParent(par) -> par).toMap.filter(_._1.isInlineTrait) + val ancestors: List[ClassSymbol] = cls.tpe.baseClasses.filter(sym => sym.isInlineTrait && sym != cls.symbol) + ancestors.flatMap(ancestor => + def baseTree = + cls.tpe.baseType(ancestor) match + case AppliedType(tycon, targs) => + Some(AppliedTypeTree(TypeTree(tycon), targs.map(TypeTree(_)))) + case tref: TypeRef => + Some(Ident(tref)) + case baseTpe => + report.error(s"unknown base type ${baseTpe.show} for ancestor ${ancestor.show} of ${cls.symbol.show}") + None + parentTrees.get(ancestor).orElse(baseTree.map(_.withSpan(cls.span))) + ) + case _ => + Nil } private def needsTransparentInlining(tree: Tree)(using Context): Boolean = @@ -169,6 +213,26 @@ object Inlines: tree2 end inlineCall + def inlineParentInlineTraits(cls: Tree)(using Context): Tree = + cls match { + case cls @ tpd.TypeDef(_, impl: Template) => + val clsOverriddenSyms = cls.symbol.info.decls.toList.flatMap(_.allOverriddenSymbols).toSet + val newDefs = inContext(ctx.withOwner(cls.symbol)) { + inlineTraitAncestors(cls).foldLeft((List.empty[Tree], impl.body)){ + case ((inlineDefs, childDefs), parent) => + val parentTraitInliner = InlineParentTrait(parent) + val overriddenSymbols = clsOverriddenSyms ++ inlineDefs.flatMap(_.symbol.allOverriddenSymbols) + val inlinedDefs1 = inlineDefs ::: parentTraitInliner.expandDefs(overriddenSymbols) + val childDefs1 = parentTraitInliner.adaptDefs(childDefs) // TODO do this outside of inlining: we need to adapt ALL references to inlined stuff + (inlinedDefs1, childDefs1) + } + } + val impl1 = cpy.Template(impl)(body = newDefs._1 ::: newDefs._2) + cpy.TypeDef(cls)(rhs = impl1) + case _ => + cls + } + /** Try to inline a pattern with an inline unapply method. Fail with error if the maximal * inline depth is exceeded. * @@ -459,4 +523,245 @@ object Inlines: // the opaque type itself. An example is in pos/opaque-inline1.scala. end expand end InlineCall + + private class InlineParentTrait(parent: tpd.Tree)(using Context) extends Inliner(parent): + import tpd._ + import Inlines.* + + private val parentSym = symbolFromParent(parent) + private val paramAccessorsMapper = ParamAccessorsMapper() + private val innerClassNewSyms: mutable.LinkedHashMap[Symbol, Symbol] = mutable.LinkedHashMap.empty + + private val childThisType = ctx.owner.thisType + private val childThisTree = This(ctx.owner.asClass).withSpan(parent.span) + + def expandDefs(overriddenDecls: Set[Symbol]): List[Tree] = + paramAccessorsMapper.registerParamValuesOf(parent) + val stats = Inlines.defsToInline(parentSym).filterNot(stat => overriddenDecls.contains(stat.symbol)) + stats.map{ + case member: MemberDef => Left((member, inlinedSym(member.symbol))) // Private symbols must be entered before the RHSs are inlined + case stat => Right(stat) + }.map{ + case Left((tree, inlinedSym)) => expandStat(tree, inlinedSym) + case Right(tree) => inlinedRhs(tree) + } + end expandDefs + + def adaptDefs(definitions: List[Tree]): List[Tree] = definitions.mapconserve(defsAdapter(_)) + + protected class InlineTraitTypeMap extends InlinerTypeMap { + override def apply(t: Type) = super.apply(t) match { + case t: ThisType if t.cls == parentSym => childThisType + case t => mapOver(t) + } + } + + protected class InlineTraitTreeMap extends InlinerTreeMap { + override def apply(tree: Tree) = super.apply(tree) match { + case tree: This if tree.symbol == parentSym => + Inlined(EmptyTree, Nil, childThisTree).withSpan(parent.span) + case tree: This => + tree.tpe match { + case thisTpe: ThisType if thisTpe.cls.isInlineTrait => + integrate(This(ctx.owner.asClass).withSpan(parent.span), thisTpe.cls) + case _ => + tree + } + case Select(qual, name) => + paramAccessorsMapper.getParamAccessorName(qual.symbol, name) match { + case Some(newName) => Select(this(qual), newName).withSpan(parent.span) + case None => Select(this(qual), name) + } + case tree => + tree + } + } + + override protected val inlinerTypeMap: InlinerTypeMap = InlineTraitTypeMap() + override protected val inlinerTreeMap: InlinerTreeMap = InlineTraitTreeMap() + + override protected def substFrom: List[Symbol] = innerClassNewSyms.keys.toList + override protected def substTo: List[Symbol] = innerClassNewSyms.values.toList + override protected def inlineCopier: tpd.TreeCopier = new TypedTreeCopier() { + // FIXME it feels weird... Is this correct? + override def Apply(tree: Tree)(fun: Tree, args: List[Tree])(using Context): Apply = + untpd.cpy.Apply(tree)(fun, args).withTypeUnchecked(tree.tpe) + } + + override protected def computeThisBindings(): Unit = () + override protected def canElideThis(tpe: ThisType): Boolean = true + + override protected def inlineCtx(inlineTyper: InlineTyper)(using Context): Context = + ctx.fresh.setTyper(inlineTyper).setNewScope + + extension (sym: Symbol) + private def isTermParamAccessor: Boolean = !sym.isType && sym.is(ParamAccessor) + + private def expandStat(stat: tpd.Tree, inlinedSym: Symbol)(using Context): tpd.Tree = stat match + case stat: ValDef => + inlinedValDef(stat, inlinedSym) + case stat: DefDef => + inlinedDefDef(stat, inlinedSym) + case stat @ TypeDef(_, _: Template) => + inlinedClassDef(stat, inlinedSym.asClass) + case stat: TypeDef => + inlinedTypeDef(stat, inlinedSym) + + private def inlinedSym(sym: Symbol, withoutFlags: FlagSet = EmptyFlags)(using Context): Symbol = + if sym.isClass then inlinedClassSym(sym.asClass, withoutFlags) else inlinedMemberSym(sym, withoutFlags) + + private def inlinedClassSym(sym: ClassSymbol, withoutFlags: FlagSet = EmptyFlags)(using Context): ClassSymbol = + sym.info match { + case clsInfo: ClassInfo => + val typeParams: List[Type] = sym.primaryConstructor.info match { + case poly: PolyType => poly.paramRefs + case _ => Nil + } + // Extend inner class from inline trait to preserve typing + val newParent = ctx.owner.thisType.select(sym).appliedTo(typeParams) + val inlinedSym = newClassSymbol( + ctx.owner, + sym.name, + (sym.flags | Synthetic) &~ withoutFlags, + newCls => { + val ClassInfo(prefix, _, parents, _, selfInfo) = inlinerTypeMap.mapClassInfo(clsInfo) + ClassInfo(prefix, newCls, parents :+ newParent, Scopes.newScope, selfInfo) // TODO fix selfInfo (what to use?) + }, + sym.privateWithin, + spanCoord(parent.span) + ) + innerClassNewSyms.put(sym, inlinedSym) + inlinedSym.entered + case _ => + report.error(s"Class symbol ${sym.show} does not have class info") + sym + } + + private def inlinedMemberSym(sym: Symbol, withoutFlags: FlagSet = EmptyFlags)(using Context): Symbol = + var name = sym.name + var flags = sym.flags | Synthetic + if sym.isTermParamAccessor then flags &~= ParamAccessor + if sym.is(Local) then + name = paramAccessorsMapper.registerNewName(sym) + else + flags |= Override + sym.copy( + owner = ctx.owner, + name = name, + flags = flags &~ withoutFlags, + info = inlinerTypeMap(sym.info), + coord = spanCoord(parent.span)).entered + + private def inlinedValDef(vdef: ValDef, inlinedSym: Symbol)(using Context): ValDef = + val rhs = + paramAccessorsMapper + .getParamAccessorRhs(vdef.symbol.owner, vdef.symbol.name) + .getOrElse(inlinedRhs(vdef, inlinedSym)) + tpd.ValDef(inlinedSym.asTerm, rhs).withSpan(parent.span) + + private def inlinedDefDef(ddef: DefDef, inlinedSym: Symbol)(using Context): DefDef = + val rhsFun: List[List[Tree]] => Tree = + if ddef.symbol.isSetter then + _ => unitLiteral + else + paramss => + val oldParamSyms = ddef.paramss.flatten.map(_.symbol) + val newParamSyms = paramss.flatten.map(_.symbol) + val ddef1 = cpy.DefDef(ddef)(rhs = ddef.rhs.subst(oldParamSyms, newParamSyms)) + inlinedRhs(ddef1, inlinedSym) + tpd.DefDef(inlinedSym.asTerm, rhsFun).withSpan(parent.span) + + private def inlinedPrimaryConstructorDefDef(ddef: DefDef)(using Context): DefDef = + // TODO check if symbol must be copied + val inlinedSym = inlinedMemberSym(ddef.symbol, withoutFlags = Override) + val constr = inlinedDefDef(ddef, inlinedSym) + cpy.DefDef(constr)(tpt = TypeTree(defn.UnitType), rhs = EmptyTree) + + private def inlinedClassDef(clsDef: TypeDef, inlinedCls: ClassSymbol)(using Context): Tree = + val TypeDef(_, tmpl: Template) = clsDef: @unchecked + val (constr, body) = inContext(ctx.withOwner(inlinedCls)) { + val inlinedConstr = inlinedPrimaryConstructorDefDef(tmpl.constr) + val inlinedTmpl = tmpl.body.map { + case stat: TypeDef if stat.symbol.isAllOf(PrivateLocal | Param) => + expandStat(stat, inlinedSym(stat.symbol, withoutFlags = Override)) + case stat => + expandStat(stat, inlinedSym(stat.symbol)) + } + (inlinedConstr, inlinedTmpl) + } + val clsDef1 = tpd.ClassDefWithParents(inlinedCls, constr, tmpl.parents, body) // TODO add correct parent tree + inlined(clsDef1)._2.withSpan(clsDef.span) + + private def inlinedTypeDef(tdef: TypeDef, inlinedSym: Symbol)(using Context): TypeDef = + tpd.TypeDef(inlinedSym.asType).withSpan(parent.span) + + private def inlinedRhs(vddef: ValOrDefDef, inlinedSym: Symbol)(using Context): Tree = + val rhs = vddef.rhs.changeOwner(vddef.symbol, inlinedSym) + inlinedRhs(rhs)(using ctx.withOwner(inlinedSym)) + + private def inlinedRhs(rhs: Tree)(using Context): Tree = + if rhs.isEmpty then + rhs + else + // TODO make version of inlined that does not return bindings? + Inlined(tpd.ref(parentSym), Nil, inlined(rhs)._2).withSpan(parent.span) + + private val defsAdapter = + val typeMap = new DeepTypeMap { + override def apply(tp: Type): Type = tp match { + case TypeRef(_, sym: Symbol) if innerClassNewSyms.contains(sym) => + TypeRef(childThisType, innerClassNewSyms(sym)) + case _ => + mapOver(tp) + } + } + def treeMap(tree: Tree) = tree match { + case ident: Ident if innerClassNewSyms.contains(ident.symbol) => + Ident(innerClassNewSyms(ident.symbol).namedType) + case tdef: TypeDef if tdef.symbol.isClass => + tdef.symbol.info = typeMap(tdef.symbol.info) + tdef + case tree => + tree + } + new TreeTypeMap( + typeMap = typeMap, + treeMap = treeMap, + substFrom = substFrom, + substTo = substTo, + ) + end defsAdapter + + private class ParamAccessorsMapper: + private val paramAccessorsTrees: mutable.Map[Symbol, Map[Name, Tree]] = mutable.Map.empty + private val paramAccessorsNewNames: mutable.Map[(Symbol, Name), Name] = mutable.Map.empty + + def registerParamValuesOf(parent: Tree): Unit = + def allArgs(tree: Tree, acc: Vector[List[Tree]]): List[List[Tree]] = tree match + case Apply(fun, args) => allArgs(fun, acc :+ args) + case TypeApply(fun, _) => allArgs(fun, acc) + case _ => acc.toList + def allParams(info: Type, acc: List[List[Name]]): List[List[Name]] = info match + case mt: MethodType => allParams(mt.resultType, mt.paramNames :: acc) + case pt: PolyType => allParams(pt.resultType, acc) + case _ => acc + val info = + if parent.symbol.isClass then parent.symbol.primaryConstructor.info + else parent.symbol.info + val paramAccessors = allParams(info, Nil).flatten.zip(allArgs(parent, Vector.empty).flatten).toMap + paramAccessorsTrees.put(symbolFromParent(parent), paramAccessors) + + def registerNewName(paramAccessorSym: Symbol): paramAccessorSym.ThisName = + val oldName = paramAccessorSym.name + val newName = oldName.expandedName(parentSym) + paramAccessorsNewNames.put((paramAccessorSym.owner, oldName), newName) + newName + + def getParamAccessorRhs(parent: Symbol, paramAccessorName: Name): Option[Tree] = + paramAccessorsTrees.get(parent).flatMap(_.get(paramAccessorName)) + + def getParamAccessorName(parent: Symbol, paramAccessorName: Name): Option[Name] = + paramAccessorsNewNames.get(parent, paramAccessorName) + end ParamAccessorsMapper + end InlineParentTrait end Inlines diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 3079b26df6cd..47a782f46711 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -3765,7 +3765,7 @@ object Parsers { } } - /** TmplDef ::= ([‘case’] ‘class’ | ‘trait’) ClassDef + /** TmplDef ::= ([‘case’] ‘class’ | [‘inline’] ‘trait’) ClassDef * | [‘case’] ‘object’ ObjectDef * | ‘enum’ EnumDef * | ‘given’ GivenDef diff --git a/compiler/src/dotty/tools/dotc/transform/Constructors.scala b/compiler/src/dotty/tools/dotc/transform/Constructors.scala index 4dd7205e4ee0..d98f5ae88987 100644 --- a/compiler/src/dotty/tools/dotc/transform/Constructors.scala +++ b/compiler/src/dotty/tools/dotc/transform/Constructors.scala @@ -226,7 +226,7 @@ class Constructors extends MiniPhase with IdentityDenotTransformer { thisPhase = constrStats += intoConstr(stat, sym) } else dropped += sym - case stat @ DefDef(name, _, tpt, _) if stat.symbol.isGetter && !stat.symbol.is(Lazy) => + case stat @ DefDef(name, _, tpt, _) if stat.symbol.isGetter && !stat.symbol.is(Lazy) && !stat.symbol.owner.isInlineTrait => val sym = stat.symbol assert(isRetained(sym), sym) if sym.isConstExprFinalVal then diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala index 10f73fa94e08..8a4155175f71 100644 --- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala @@ -11,7 +11,6 @@ import dotty.tools.dotc.ast.Trees._ import dotty.tools.dotc.quoted._ import dotty.tools.dotc.inlines.Inlines import dotty.tools.dotc.ast.TreeMapWithImplicits -import dotty.tools.dotc.core.DenotTransformers.IdentityDenotTransformer import dotty.tools.dotc.staging.StagingLevel import scala.collection.mutable.ListBuffer @@ -45,7 +44,7 @@ class Inlining extends MacroTransform { new TreeTraverser { def traverse(tree: Tree)(using Context): Unit = tree match - case tree: RefTree if !Inlines.inInlineMethod && StagingLevel.level == 0 => + case tree: RefTree if !Inlines.inInlineContext && StagingLevel.level == 0 => assert(!tree.symbol.isInlineMethod, tree.show) case _ => traverseChildren(tree) diff --git a/compiler/src/dotty/tools/dotc/transform/Mixin.scala b/compiler/src/dotty/tools/dotc/transform/Mixin.scala index 5ca09dd6188f..6e6adbeeafc2 100644 --- a/compiler/src/dotty/tools/dotc/transform/Mixin.scala +++ b/compiler/src/dotty/tools/dotc/transform/Mixin.scala @@ -93,7 +93,7 @@ object Mixin { * def x_=(y: T) = () * * 4.5 (done in `mixinForwarders`) For every method - * ` def f[Ts](ps1)...(psN): U` imn M` that needs to be disambiguated: + * ` def f[Ts](ps1)...(psN): U` in M that needs to be disambiguated: * * def f[Ts](ps1)...(psN): U = super[M].f[Ts](ps1)...(psN) * @@ -174,6 +174,7 @@ class Mixin extends MiniPhase with SymTransformer { thisPhase => sym.isGetter && !wasOneOf(sym, DeferredOrLazy | ParamAccessor) && atPhase(thisPhase) { !sym.setter.exists } && !sym.isConstExprFinalVal + && !sym.owner.isInlineTrait private def makeTraitSetter(getter: TermSymbol)(using Context): Symbol = getter.copy( @@ -244,6 +245,7 @@ class Mixin extends MiniPhase with SymTransformer { thisPhase => transformFollowingDeep(superRef(baseCls.primaryConstructor).appliedToNone) :: Nil def traitInits(mixin: ClassSymbol): List[Tree] = { + if mixin.isInlineTrait then return Nil val argsIt = superCallsAndArgs.get(mixin) match case Some((_, _, args)) => args.iterator case _ => Iterator.empty @@ -287,7 +289,7 @@ class Mixin extends MiniPhase with SymTransformer { thisPhase => def setters(mixin: ClassSymbol): List[Tree] = val mixinSetters = mixin.info.decls.filter { sym => - sym.isSetter && (!wasOneOf(sym, Deferred) || sym.name.is(TraitSetterName)) + sym.isSetter && (!wasOneOf(sym, Deferred) || sym.name.is(TraitSetterName)) && !sym.owner.isInlineTrait } for (setter <- mixinSetters) yield transformFollowing(DefDef(mkForwarderSym(setter.asTerm), unitLiteral.withSpan(cls.span))) diff --git a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala index 8b58f18bca52..34a11ebb3844 100644 --- a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala @@ -81,9 +81,9 @@ class PickleQuotes extends MacroTransform { override def checkPostCondition(tree: Tree)(using Context): Unit = tree match case tree: Quote => - assert(Inlines.inInlineMethod) + assert(Inlines.inInlineContext) case tree: Splice => - assert(Inlines.inInlineMethod) + assert(Inlines.inInlineContext) case _ => override def run(using Context): Unit = diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index ea5e7db2c088..0b8bc405f127 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -155,6 +155,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase tree match case tree: ValOrDefDef if !sym.is(Synthetic) => checkInferredWellFormed(tree.tpt) + if tree.symbol.owner.isInlineTrait then checkInlTraitPrivateMemberIsLocal(tree) if sym.is(Method) then if sym.isSetter then sym.keepAnnotationsCarrying(thisPhase, Set(defn.SetterMetaAnnot)) @@ -192,6 +193,9 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase => Checking.checkAppliedTypesIn(tree) case _ => + private def checkInlTraitPrivateMemberIsLocal(tree: Tree)(using Context): Unit = + if tree.symbol.owner.isInlineTrait && tree.symbol.isAllOf(Private, butNot = Local) then + report.error(em"implementation restriction: inline traits cannot have non-local private members", tree.srcPos) private def transformSelect(tree: Select, targs: List[Tree])(using Context): Tree = { val qual = tree.qualifier @@ -388,6 +392,8 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase val tree1 = cpy.DefDef(tree)(rhs = normalizeErasedRhs(tree.rhs, tree.symbol)) processValOrDefDef(superAcc.wrapDefDef(tree1)(super.transform(tree1).asInstanceOf[DefDef])) case tree: TypeDef => + if tree.symbol.isInlineTrait then + ctx.compilationUnit.needsInlining = true // Transform inner classes to traits registerIfHasMacroAnnotations(tree) val sym = tree.symbol if (sym.isClass) @@ -399,6 +405,8 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase tree.rhs match case impl: Template => for parent <- impl.parents do + if Inlines.symbolFromParent(parent).isInlineTrait then + ctx.compilationUnit.needsInlining = true Checking.checkTraitInheritance(parent.tpe.classSymbol, sym.asClass, parent.srcPos) // Constructor parameters are in scope when typing a parent. // While they can safely appear in a parent tree, to preserve diff --git a/compiler/src/dotty/tools/dotc/transform/PruneInlineTraits.scala b/compiler/src/dotty/tools/dotc/transform/PruneInlineTraits.scala new file mode 100644 index 000000000000..5d9db954201a --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/PruneInlineTraits.scala @@ -0,0 +1,48 @@ +package dotty.tools.dotc +package transform + +import core._ +import Contexts._ +import DenotTransformers.SymTransformer +import Flags._ +import SymDenotations._ +import Symbols._ +import MegaPhase.MiniPhase +import ast.tpd + +class PruneInlineTraits extends MiniPhase with SymTransformer { thisTransform => + import tpd._ + import PruneInlineTraits._ + + override def phaseName: String = PruneInlineTraits.name + + override def description: String = PruneInlineTraits.description + + override def transformSym(sym: SymDenotation)(using Context): SymDenotation = + if isEraseable(sym) then sym.copySymDenotation(initFlags = sym.flags | Deferred) + else sym + + override def transformValDef(tree: ValDef)(using Context): ValDef = + if isEraseable(tree.symbol) then cpy.ValDef(tree)(rhs = EmptyTree) + else tree + + override def transformDefDef(tree: DefDef)(using Context): DefDef = + if isEraseable(tree.symbol) then cpy.DefDef(tree)(rhs = EmptyTree) + else tree + + private def isEraseable(sym: SymDenotation)(using Context): Boolean = + !sym.isType + && !sym.isConstructor + && !sym.is(Param) + && !sym.is(ParamAccessor) + && !sym.is(Private) + && !sym.isLocalDummy + && sym.owner.isInlineTrait +} + +object PruneInlineTraits { + import tpd._ + + val name: String = "pruneInlineTraits" + val description: String = "drop rhs definitions in inline traits" +} diff --git a/compiler/src/dotty/tools/dotc/transform/SpecializeInlineTraits.scala b/compiler/src/dotty/tools/dotc/transform/SpecializeInlineTraits.scala new file mode 100644 index 000000000000..1a2819eb3e38 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/SpecializeInlineTraits.scala @@ -0,0 +1,124 @@ +package dotty.tools.dotc +package transform + +import core._ +import Flags._ +import Contexts._ +import Symbols._ +import SymUtils._ +import dotty.tools.dotc.ast.tpd +import dotty.tools.dotc.ast.Trees._ +import dotty.tools.dotc.quoted._ +import dotty.tools.dotc.inlines.Inlines +import dotty.tools.dotc.ast.TreeMapWithImplicits +import dotty.tools.dotc.core.DenotTransformers.SymTransformer +import dotty.tools.dotc.staging.StagingLevel +import dotty.tools.dotc.core.SymDenotations.SymDenotation +import dotty.tools.dotc.core.StdNames.{str, nme} +import dotty.tools.dotc.core.Types.* +import dotty.tools.dotc.core.Names.{Name, TermName} + +import scala.collection.mutable.ListBuffer + +class SpecializeInlineTraits extends MacroTransform, SymTransformer { + + import tpd._ + + override def phaseName: String = SpecializeInlineTraits.name + + override def description: String = SpecializeInlineTraits.description + + override def changesMembers: Boolean = true + + override def changesParents: Boolean = true + + override def run(using Context): Unit = + try super.run + catch case _: CompilationUnit.SuspendException => () + + override def newTransformer(using Context): Transformer = new Transformer { + override def transform(tree: Tree)(using Context): Tree = tree match { + case tree: TypeDef if tree.symbol.isInlineTrait => + transformInlineTrait(tree) + case tree: TypeDef if Inlines.needsInlining(tree) => + val tree1 = super.transform(tree).asInstanceOf[TypeDef] + if tree1.tpe.isError then tree1 + else if tree1.symbol.isInlineTrait then transformInlineTrait(tree1) + else Inlines.inlineParentInlineTraits(tree1) + case _ => super.transform(tree) + } + } + + override def transformSym(symd: SymDenotation)(using Context): SymDenotation = + if symd.isClass && symd.owner.isInlineTrait && !symd.is(Module) then + symd.copySymDenotation(name = SpecializeInlineTraits.newInnerClassName(symd.name), initFlags = (symd.flags &~ Final) | Trait) + else + symd + + override def checkPostCondition(tree: Tree)(using Context): Unit = + tree match { + // TODO check that things are inlined properly + case _ => + } + + private def transformInlineTrait(inlineTrait: TypeDef)(using Context): TypeDef = + val tpd.TypeDef(_, tmpl: Template) = inlineTrait: @unchecked + val body1 = tmpl.body.flatMap { + case innerClass: TypeDef if innerClass.symbol.isClass => + val newTrait = makeTraitFromInnerClass(innerClass) + val newType = makeTypeFromInnerClass(inlineTrait.symbol, innerClass, newTrait.symbol) + List(newTrait, newType) + case member: MemberDef => + List(member) + case _ => + // Remove non-memberdefs, as they are normally placed into $init() + Nil + } + val tmpl1 = cpy.Template(tmpl)(body = body1) + cpy.TypeDef(inlineTrait)(rhs = tmpl1) + end transformInlineTrait + + private def makeTraitFromInnerClass(innerClass: TypeDef)(using Context): TypeDef = + val TypeDef(name, tmpl: Template) = innerClass: @unchecked + val newInnerParents = tmpl.parents.mapConserve(ConcreteParentStripper.apply) + val tmpl1 = cpy.Template(tmpl)(parents = newInnerParents) // TODO .withType(???) + val newTrait = cpy.TypeDef(innerClass)(name = SpecializeInlineTraits.newInnerClassName(name), rhs = tmpl1) + newTrait.symbol.setFlag(Synthetic) + newTrait + end makeTraitFromInnerClass + + private def makeTypeFromInnerClass(parentSym: Symbol, innerClass: TypeDef, newTraitSym: Symbol)(using Context): TypeDef = + val upperBound = innerClass.symbol.primaryConstructor.info match { + case _: MethodType => + newTraitSym.typeRef + case poly: PolyType => + HKTypeLambda(poly.paramNames)(tl => poly.paramInfos, tl => newTraitSym.typeRef.appliedTo(tl.paramRefs.head)) + } + val newTypeSym = newSymbol( + owner = parentSym, + name = newTraitSym.name.asTypeName, + flags = innerClass.symbol.flags & (Private | Protected) | Synthetic, + info = TypeBounds.upper(upperBound), + privateWithin = innerClass.symbol.privateWithin, + coord = innerClass.symbol.coord, + nestingLevel = innerClass.symbol.nestingLevel, + ).asType + TypeDef(newTypeSym) + end makeTypeFromInnerClass + + private object ConcreteParentStripper extends TreeAccumulator[Tree] { + def apply(tree: Tree)(using Context): Tree = apply(tree, tree) + + override def apply(x: Tree, tree: Tree)(using Context): Tree = tree match { + case ident: Ident => ident + case tpt: TypeTree => tpt + case _ => foldOver(x, tree) + } + } +} + +object SpecializeInlineTraits: + val name: String = "specializeInlineTraits" + val description: String = "inline the code of inline traits and specialize calls to their members" + + private[transform] def newInnerClassName(name: Name): name.ThisName = name ++ str.INLINE_TRAIT_INNER_CLASS_SUFFIX diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index ff5dc5042eaf..860d389a181e 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -90,8 +90,8 @@ class Splicing extends MacroTransform: case tree: Quote => val body1 = QuoteTransformer().transform(tree.body)(using quoteContext) cpy.Quote(tree)(body1, tree.tags) - case tree: DefDef if tree.symbol.is(Inline) => - // Quotes in inlined methods are only pickled after they are inlined. + case _: DefDef | _: TypeDef if tree.symbol.is(Inline) => + // Quotes in inlined methods and traits are only pickled after they are inlined. tree case _ => super.transform(tree) diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 2869f64f33d0..8d56fb728c0c 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -548,7 +548,7 @@ object Checking { if (sym.isConstructor && !sym.isPrimaryConstructor && sym.owner.is(Trait, butNot = JavaDefined)) val addendum = if ctx.settings.Ydebug.value then s" ${sym.owner.flagsString}" else "" fail(em"Traits cannot have secondary constructors$addendum") - checkApplicable(Inline, sym.isTerm && !sym.isOneOf(Mutable | Module)) + checkApplicable(Inline, sym.isTerm && !sym.isOneOf(Mutable | Module) || sym.is(Trait)) checkApplicable(Lazy, !sym.isOneOf(Method | Mutable)) if (sym.isType && !sym.isOneOf(Deferred | JavaDefined)) for (cls <- sym.allOverriddenSymbols.filter(_.isClass)) { diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index fe28a8b18833..bc4096586c8c 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -340,6 +340,8 @@ object RefChecks { * of class `clazz` are met. */ def checkOverride(checkSubType: (Type, Type) => Context ?=> Boolean, member: Symbol, other: Symbol): Unit = + def isInlinedFromInlineTrait = other.owner.isAllOf(InlineTrait) && member.is(Synthetic) + def memberTp(self: Type) = if (member.isClass) TypeAlias(member.typeRef.EtaExpand(member.typeParams)) else self.memberInfo(member) @@ -415,12 +417,13 @@ object RefChecks { def overrideTargetNameError() = val otherTargetName = i"@targetName(${other.targetName})" - if member.hasTargetName(member.name) then - overrideError(i"misses a target name annotation $otherTargetName") - else if other.hasTargetName(other.name) then - overrideError(i"should not have a @targetName annotation since the overridden member hasn't one either") - else - overrideError(i"has a different target name annotation; it should be $otherTargetName") + if !isInlinedFromInlineTrait then + if member.hasTargetName(member.name) then + overrideError(i"misses a target name annotation $otherTargetName") + else if other.hasTargetName(other.name) then + overrideError(i"should not have a @targetName annotation since the overridden member hasn't one either") + else + overrideError(i"has a different target name annotation; it should be $otherTargetName") //Console.println(infoString(member) + " overrides " + infoString(other) + " in " + clazz);//DEBUG @@ -459,13 +462,13 @@ object RefChecks { // direct overrides were already checked on completion (see Checking.chckWellFormed) // the test here catches indirect overriddes between two inherited base types. overrideError("cannot be used here - class definitions cannot be overridden") - else if (other.isOpaqueAlias) + else if (other.isOpaqueAlias && !isInlinedFromInlineTrait) // direct overrides were already checked on completion (see Checking.chckWellFormed) // the test here catches indirect overriddes between two inherited base types. overrideError("cannot be used here - opaque type aliases cannot be overridden") else if (!other.is(Deferred) && member.isClass) overrideError("cannot be used here - classes can only override abstract types") - else if other.isEffectivelyFinal then // (1.2) + else if (other.isEffectivelyFinal && !isInlinedFromInlineTrait) then // (1.2) overrideError(i"cannot override final member ${other.showLocated}") else if (member.is(ExtensionMethod) && !other.is(ExtensionMethod)) // (1.3) overrideError("is an extension method, cannot override a normal method") @@ -502,7 +505,7 @@ object RefChecks { overrideError("needs `override` modifier") else if (other.is(AbsOverride) && other.isIncompleteIn(clazz) && !member.is(AbsOverride)) overrideError("needs `abstract override` modifiers") - else if member.is(Override) && other.is(Mutable) then + else if member.is(Override) && other.is(Mutable) && !isInlinedFromInlineTrait then overrideError("cannot override a mutable variable") else if (member.isAnyOverride && !(member.owner.thisType.baseClasses exists (_ isSubClass other.owner)) && diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index cb23262d1410..098f7ffe3c2c 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2669,6 +2669,29 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val dummy = localDummy(cls, impl) val body1 = addAccessorDefs(cls, typedStats(impl.body, dummy)(using ctx.inClassContext(self1.symbol))._1) + if !ctx.isAfterTyper && cls.isInlineTrait then + body1.map(_.symbol).filter(_.isInlineTrait).foreach(innerInlTrait => + report.error( + em"Implementation restriction: an inline trait cannot be defined inside of another inline trait", + innerInlTrait.srcPos + ) + ) + val membersToInline = body1.filter(member => Inlines.isInlineableFromInlineTrait(cls, member)) + membersToInline.foreach { + case tdef: TypeDef if tdef.symbol.isClass => + def rec(paramss: List[List[Symbol]]): Unit = paramss match { + case (param :: _) :: _ if param.isTerm => + report.error(em"Implementation restriction: inner classes inside inline traits cannot have term parameters", param.srcPos) + case _ :: paramss => + rec(paramss) + case _ => + } + rec(tdef.symbol.primaryConstructor.paramSymss) + case _ => + } + val wrappedMembersToInline = Block(membersToInline, unitLiteral).withSpan(cdef.span) + PrepareInlineable.registerInlineInfo(cls, wrappedMembersToInline) + checkNoDoubleDeclaration(cls) val impl1 = cpy.Template(impl)(constr1, parents1, Nil, self1, body1) .withType(dummy.termRef) diff --git a/tests/disabled/pos/inline-trait-3-trait-with-params.scala b/tests/disabled/pos/inline-trait-3-trait-with-params.scala new file mode 100644 index 000000000000..a497ebbcb7c1 --- /dev/null +++ b/tests/disabled/pos/inline-trait-3-trait-with-params.scala @@ -0,0 +1,13 @@ +inline trait A[T](a: T): + def f: T = a + def f(x: T): T = x + def f[U <: T](x: U, y: T): T = x +end A + +class B extends A[Int](3): + /* + override def f: Int = ??? + override def f(x: Int): Int = ??? + override def f[U <: Int](x: U, y: Int): Int = ??? + */ +end B diff --git a/tests/disabled/pos/inline-trait-4-inner-class.scala b/tests/disabled/pos/inline-trait-4-inner-class.scala new file mode 100644 index 000000000000..b3c25e75de4c --- /dev/null +++ b/tests/disabled/pos/inline-trait-4-inner-class.scala @@ -0,0 +1,20 @@ +inline trait Options[+T]: + sealed trait Option: + def get: T + def isEmpty: Boolean + + class Some(x: T) extends Option: + def get: T = x + def isEmpty: Boolean = false + + object None extends Option: + def get: T = throw new NoSuchElementException("None.get") + def isEmpty: Boolean = true +end Options + +object IntOptions extends Options[Int] +import IntOptions._ + +val o1: Option = Some(1) // specialized +val o2: Option = None +val x1: Int = o1.get // no unboxing diff --git a/tests/disabled/pos/inline-trait-body-class-abstract.scala b/tests/disabled/pos/inline-trait-body-class-abstract.scala new file mode 100644 index 000000000000..4704b324e26d --- /dev/null +++ b/tests/disabled/pos/inline-trait-body-class-abstract.scala @@ -0,0 +1,10 @@ +inline trait A: + class InnerA: + def foo(): Int + def bar = foo() + 1 + +class B extends A: + class InnerB extends InnerA: + def foo(): Int = -23 + + def f = InnerB().bar \ No newline at end of file diff --git a/tests/disabled/pos/inline-trait-body-class-case.scala b/tests/disabled/pos/inline-trait-body-class-case.scala new file mode 100644 index 000000000000..79bade9a9036 --- /dev/null +++ b/tests/disabled/pos/inline-trait-body-class-case.scala @@ -0,0 +1,5 @@ +inline trait A: + case class Inner(val x: Int) + +class B extends A: + def f = Inner(17).x \ No newline at end of file diff --git a/tests/disabled/pos/inline-trait-body-class-enum.scala b/tests/disabled/pos/inline-trait-body-class-enum.scala new file mode 100644 index 000000000000..a114ff396067 --- /dev/null +++ b/tests/disabled/pos/inline-trait-body-class-enum.scala @@ -0,0 +1,6 @@ +inline trait A: + enum Inner: + case A, B, C + +class B extends A: + def f = Inner.B \ No newline at end of file diff --git a/tests/disabled/pos/inline-trait-body-class-extends-inline-trait.scala b/tests/disabled/pos/inline-trait-body-class-extends-inline-trait.scala new file mode 100644 index 000000000000..1f1a3836fa98 --- /dev/null +++ b/tests/disabled/pos/inline-trait-body-class-extends-inline-trait.scala @@ -0,0 +1,11 @@ +inline trait A: + class Inner extends Trait[Int]: + val x = 1 + +inline trait Trait[T]: + def f(x: T): T = x + +class B extends A: + val inner = Inner() + def x = inner.x + def f = inner.f(x) \ No newline at end of file diff --git a/tests/disabled/pos/inline-trait-body-class-generic.scala b/tests/disabled/pos/inline-trait-body-class-generic.scala new file mode 100644 index 000000000000..91e90a53e5ef --- /dev/null +++ b/tests/disabled/pos/inline-trait-body-class-generic.scala @@ -0,0 +1,6 @@ +inline trait A[T]: + class Inner[U](u: U): + val x: (T, U) = (???, u) + def f: (T, String) = Inner("U").x + +class B extends A[Int] \ No newline at end of file diff --git a/tests/disabled/pos/inline-trait-body-class-object.scala b/tests/disabled/pos/inline-trait-body-class-object.scala new file mode 100644 index 000000000000..dc990c69573c --- /dev/null +++ b/tests/disabled/pos/inline-trait-body-class-object.scala @@ -0,0 +1,6 @@ +inline trait A[T]: + object Inner: + val x: T = ??? + +class B extends A[Int]: + def i: Int = Inner.x diff --git a/tests/disabled/pos/inline-trait-body-class-params.scala b/tests/disabled/pos/inline-trait-body-class-params.scala new file mode 100644 index 000000000000..114d8811da2d --- /dev/null +++ b/tests/disabled/pos/inline-trait-body-class-params.scala @@ -0,0 +1,5 @@ +inline trait A: + class Inner(val x: Int) + +class B extends A: + def f = Inner(17).x \ No newline at end of file diff --git a/tests/disabled/pos/inline-trait-body-class-sealed.scala b/tests/disabled/pos/inline-trait-body-class-sealed.scala new file mode 100644 index 000000000000..ff28d164c617 --- /dev/null +++ b/tests/disabled/pos/inline-trait-body-class-sealed.scala @@ -0,0 +1,7 @@ +inline trait A: + sealed class InnerA: + val x = 1 + +class B extends A: + class InnerB extends InnerA + def f = InnerB().x \ No newline at end of file diff --git a/tests/disabled/pos/inline-trait-body-class-simple.scala b/tests/disabled/pos/inline-trait-body-class-simple.scala new file mode 100644 index 000000000000..56476b96b44b --- /dev/null +++ b/tests/disabled/pos/inline-trait-body-class-simple.scala @@ -0,0 +1,6 @@ +inline trait A: + class Inner: + val x = 1 + +class B extends A: + def f = Inner().x \ No newline at end of file diff --git a/tests/disabled/pos/inline-trait-body-trait-generic.scala b/tests/disabled/pos/inline-trait-body-trait-generic.scala new file mode 100644 index 000000000000..999dd0c8c1ea --- /dev/null +++ b/tests/disabled/pos/inline-trait-body-trait-generic.scala @@ -0,0 +1,6 @@ +inline trait A[T]: + trait InnerA[U]: + def x: (T, U) = ??? + +class B extends A[Int]: + class InnerB extends InnerA[String] \ No newline at end of file diff --git a/tests/neg/i2421.scala b/tests/neg/i2421.scala index dc8e229f38f0..bcf3da6dbb36 100644 --- a/tests/neg/i2421.scala +++ b/tests/neg/i2421.scala @@ -1,7 +1,6 @@ inline object Foo // OK (error would be detected later, in PostTyper) inline class Bar // error: modifier(s) `inline' incompatible with type definition inline abstract class Baz // error: modifier(s) `inline' incompatible with type definition -inline trait Qux // error: modifier(s) `inline' incompatible with type definition object Quux { inline type T // error: modifier(s) `inline' incompatible with type definition diff --git a/tests/neg/inline-trait-body-override-def-final.scala b/tests/neg/inline-trait-body-override-def-final.scala new file mode 100644 index 000000000000..c67540a09924 --- /dev/null +++ b/tests/neg/inline-trait-body-override-def-final.scala @@ -0,0 +1,5 @@ +inline trait A: + final def f(x: Int) = x + +class B extends A: + override final def f(x: Int) = x + 1 // error \ No newline at end of file diff --git a/tests/neg/inline-trait-body-override-val-final.scala b/tests/neg/inline-trait-body-override-val-final.scala new file mode 100644 index 000000000000..32d20c4b6696 --- /dev/null +++ b/tests/neg/inline-trait-body-override-val-final.scala @@ -0,0 +1,5 @@ +inline trait A: + final val x = 1 + +class B extends A: + override final val x = 2 // error \ No newline at end of file diff --git a/tests/neg/inline-trait-body-override-var.scala b/tests/neg/inline-trait-body-override-var.scala new file mode 100644 index 000000000000..4ad8b79db477 --- /dev/null +++ b/tests/neg/inline-trait-body-override-var.scala @@ -0,0 +1,5 @@ +inline trait A: + var x: Int = 1 + +class B extends A: + override var x = 2 // error diff --git a/tests/neg/inline-trait-body-private-name-collision.scala b/tests/neg/inline-trait-body-private-name-collision.scala new file mode 100644 index 000000000000..1e3d14480d82 --- /dev/null +++ b/tests/neg/inline-trait-body-private-name-collision.scala @@ -0,0 +1,3 @@ +inline trait A: + private val x: Int = 1 // error + def eq(o: A) = o.x == x diff --git a/tests/neg/inline-trait-body-trait-inline.scala b/tests/neg/inline-trait-body-trait-inline.scala new file mode 100644 index 000000000000..82144e4c9c4d --- /dev/null +++ b/tests/neg/inline-trait-body-trait-inline.scala @@ -0,0 +1,7 @@ +inline trait A[T]: + inline trait InnerA[U]: // error + val x: (T, U) = ??? + +class B extends A[Int]: + class InnerB extends InnerA[String] + def f: (Int, String) = InnerB().x \ No newline at end of file diff --git a/tests/neg/inline-trait-body-trait-parameter.scala b/tests/neg/inline-trait-body-trait-parameter.scala new file mode 100644 index 000000000000..ec11c5b9052e --- /dev/null +++ b/tests/neg/inline-trait-body-trait-parameter.scala @@ -0,0 +1,7 @@ +inline trait A[T]: + trait InnerAType[T >: Int <: AnyVal] + trait InnerATypes[T <: AnyVal, U <: T] + trait InnerATerm(i: Int) // error + trait InnerATerms(i: Int, j: Double) // error + trait InnerATermsCurried(i: Int, j: Double)(k: String) // error + trait InnerAAllCurried[T, U](i: T, j: U)(k: (T, U)) // error \ No newline at end of file diff --git a/tests/neg/inline-trait-body-trait-term-parameters.scala b/tests/neg/inline-trait-body-trait-term-parameters.scala new file mode 100644 index 000000000000..ef7c96baf6c4 --- /dev/null +++ b/tests/neg/inline-trait-body-trait-term-parameters.scala @@ -0,0 +1,7 @@ + +inline trait A[T]: + trait InnerA(t: T): // error + def x: T = t + +class B extends A[Int]: + class InnerB extends InnerA(???) \ No newline at end of file diff --git a/tests/pos/inline-trait-1-simple-trait.scala b/tests/pos/inline-trait-1-simple-trait.scala new file mode 100644 index 000000000000..5a58b7ea2d4c --- /dev/null +++ b/tests/pos/inline-trait-1-simple-trait.scala @@ -0,0 +1,36 @@ +inline trait A: + type X = String + + val x: Int = 3 + val y: Int = x + z + private val z: Int = 1 + + def f: Int = g + def f(x: Int): Int = x + def f[T](x: T): Int = 2 + + private def g = 1 + protected[this] def p = 123 + private[this] def pp = 123456 + + def xx: X = "foo".asInstanceOf[X] +end A + +class B extends A: + /* + override type X = String + + override val x: Int = 3 + override val y: Int = this.x.+(this.z) + + private[this] val z: Int = 1 + + override def f: Int = this.g + override def f(x: Int): Int = x + override def f[T](x: T): Int = 2 + + private[this] def g: Int = 1 + + override def xx: X = "foo".asInstanceOf[X] + */ +end B diff --git a/tests/pos/inline-trait-2-generic-trait.scala b/tests/pos/inline-trait-2-generic-trait.scala new file mode 100644 index 000000000000..0df576c9c124 --- /dev/null +++ b/tests/pos/inline-trait-2-generic-trait.scala @@ -0,0 +1,13 @@ +inline trait A[T]: + def f: T = f + def f(x: T): T = x + def f[U <: T](x: U, y: T): T = x +end A + +class B extends A[Int]: + /* + override def f: Int = this.f + override def f(x: Int): Int = x + override def f[U <: Int](x: U, y: Int): Int = x + */ +end B diff --git a/tests/pos/inline-trait-3-trait-params.scala b/tests/pos/inline-trait-3-trait-params.scala new file mode 100644 index 000000000000..e2119bf9757b --- /dev/null +++ b/tests/pos/inline-trait-3-trait-params.scala @@ -0,0 +1,12 @@ +inline trait A(a: Int): + def f: Int = a + def g(b: Int): Int = a + b +end A + +class B extends A(4): + /* + private val a: Int = 4 + override def f: Int = this.a + override def g(x: Int): Int = this.a.+(b) + */ +end B diff --git a/tests/pos/inline-trait-4-no-inner-class.scala b/tests/pos/inline-trait-4-no-inner-class.scala new file mode 100644 index 000000000000..063f8e654fe5 --- /dev/null +++ b/tests/pos/inline-trait-4-no-inner-class.scala @@ -0,0 +1,24 @@ +inline trait Option[+T]: + def get: T + def isEmpty: Boolean +end Option + +inline trait Some[+T](x: T) extends Option[T]: + def get: T = x + def isEmpty: Boolean = false +end Some + +inline trait None extends Option[Nothing]: + def get: Nothing = throw new NoSuchElementException("None.get") + def isEmpty: Boolean = true +end None + +sealed trait IntOption extends Option[Int] +class IntSome(i: Int) extends IntOption, Some[Int](i) +object IntNone extends IntOption, None + +val o1: IntOption = IntSome(1) // specialized +val o2: IntOption = IntNone +val o3: Some[Int] = IntSome(1) // non-specialized +val x1: Int = o1.get // no unboxing +val x3: Int = o3.get // unboxing diff --git a/tests/pos/inline-trait-body-abstract-def.scala b/tests/pos/inline-trait-body-abstract-def.scala new file mode 100644 index 000000000000..5b675a845236 --- /dev/null +++ b/tests/pos/inline-trait-body-abstract-def.scala @@ -0,0 +1,6 @@ +inline trait A[T]: + def x: T + +class B extends A[Int]: + def x = 1 + def f: Int = x \ No newline at end of file diff --git a/tests/pos/inline-trait-body-class-simple.scala b/tests/pos/inline-trait-body-class-simple.scala new file mode 100644 index 000000000000..0f126d75409c --- /dev/null +++ b/tests/pos/inline-trait-body-class-simple.scala @@ -0,0 +1,6 @@ +inline trait A[T]: + class InnerA: + def x: T = ??? + +class B extends A[Int]: + class InnerB extends InnerA \ No newline at end of file diff --git a/tests/pos/inline-trait-body-def-context-bound.scala b/tests/pos/inline-trait-body-def-context-bound.scala new file mode 100644 index 000000000000..c932500b62c5 --- /dev/null +++ b/tests/pos/inline-trait-body-def-context-bound.scala @@ -0,0 +1,6 @@ +inline trait A: + given List[String] = "AAA" :: Nil + def foo[T: List](x: T): T = summon[List[T]].headOption.getOrElse(x) + +class B extends A: + def f = foo("BBB") \ No newline at end of file diff --git a/tests/pos/inline-trait-body-def-curried-params.scala b/tests/pos/inline-trait-body-def-curried-params.scala new file mode 100644 index 000000000000..d253aa035331 --- /dev/null +++ b/tests/pos/inline-trait-body-def-curried-params.scala @@ -0,0 +1,8 @@ +inline trait A: + def x(foo: Int)(bar: Int) = + foo + bar + + def y(foo: Int) = x(foo)(foo) + +class B extends A: + def f = x(1)(2) diff --git a/tests/pos/inline-trait-body-def-extension-method.scala b/tests/pos/inline-trait-body-def-extension-method.scala new file mode 100644 index 000000000000..77b39dc502aa --- /dev/null +++ b/tests/pos/inline-trait-body-def-extension-method.scala @@ -0,0 +1,6 @@ +inline trait A: + extension [T](x: T) + def foo[U](y: U)(z: T): (T, U, T) = (x, y, z) + +class B extends A: + def f = 1.foo("2")(3) \ No newline at end of file diff --git a/tests/pos/inline-trait-body-def-final.scala b/tests/pos/inline-trait-body-def-final.scala new file mode 100644 index 000000000000..18bdeed55a1f --- /dev/null +++ b/tests/pos/inline-trait-body-def-final.scala @@ -0,0 +1,4 @@ +inline trait A: + final def f(x: Int) = x + +class B extends A \ No newline at end of file diff --git a/tests/pos/inline-trait-body-def-generic-singleton.scala b/tests/pos/inline-trait-body-def-generic-singleton.scala new file mode 100644 index 000000000000..2862ff3b1e93 --- /dev/null +++ b/tests/pos/inline-trait-body-def-generic-singleton.scala @@ -0,0 +1,7 @@ +inline trait A: + def foo: 3 = 3 + def bar[T](x: T): T = x + +class B extends A: + def f: "A" = bar("A") + def g: 3 = foo \ No newline at end of file diff --git a/tests/pos/inline-trait-body-def-generic.scala b/tests/pos/inline-trait-body-def-generic.scala new file mode 100644 index 000000000000..75d932f2f04a --- /dev/null +++ b/tests/pos/inline-trait-body-def-generic.scala @@ -0,0 +1,6 @@ +inline trait A: + def foo[T] = 1 + def bar: [U <: A] => U => Int = [U <: A] => (x: U) => x.foo + +class B extends A: + def f = foo[String] + bar(this) \ No newline at end of file diff --git a/tests/pos/inline-trait-body-def-implicit.scala b/tests/pos/inline-trait-body-def-implicit.scala new file mode 100644 index 000000000000..51381b394cef --- /dev/null +++ b/tests/pos/inline-trait-body-def-implicit.scala @@ -0,0 +1,6 @@ +inline trait A: + implicit val x: String = "AAA" + def foo(implicit s: String): String = s + s + +class B extends A: + def f = foo \ No newline at end of file diff --git a/tests/pos/inline-trait-body-def-inline-abstract.scala b/tests/pos/inline-trait-body-def-inline-abstract.scala new file mode 100644 index 000000000000..c385c397a3f0 --- /dev/null +++ b/tests/pos/inline-trait-body-def-inline-abstract.scala @@ -0,0 +1,6 @@ +inline trait A: + inline def x: Int + +class B extends A: + inline def x = 1 + def f = x \ No newline at end of file diff --git a/tests/pos/inline-trait-body-def-inline-compiletime.scala b/tests/pos/inline-trait-body-def-inline-compiletime.scala new file mode 100644 index 000000000000..975bffc76ac2 --- /dev/null +++ b/tests/pos/inline-trait-body-def-inline-compiletime.scala @@ -0,0 +1,9 @@ +import scala.compiletime.* + +inline trait A: + inline def f[T <: String] = + inline if constValue[T] == "I consent" then "All is OK!" + else error("You must consent!") + +class B extends A: + val x = f["I consent"] \ No newline at end of file diff --git a/tests/pos/inline-trait-body-def-inline-transparent.scala b/tests/pos/inline-trait-body-def-inline-transparent.scala new file mode 100644 index 000000000000..7e48cdec1e0f --- /dev/null +++ b/tests/pos/inline-trait-body-def-inline-transparent.scala @@ -0,0 +1,5 @@ +inline trait A: + transparent inline def x = 1 + +class B extends A: + def f: 1 = x \ No newline at end of file diff --git a/tests/pos/inline-trait-body-def-inline.scala b/tests/pos/inline-trait-body-def-inline.scala new file mode 100644 index 000000000000..8b3751398365 --- /dev/null +++ b/tests/pos/inline-trait-body-def-inline.scala @@ -0,0 +1,5 @@ +inline trait A: + inline def x = 1 + +class B extends A: + def f = x \ No newline at end of file diff --git a/tests/pos/inline-trait-body-def-lambda.scala b/tests/pos/inline-trait-body-def-lambda.scala new file mode 100644 index 000000000000..4eec58d40749 --- /dev/null +++ b/tests/pos/inline-trait-body-def-lambda.scala @@ -0,0 +1,5 @@ +inline trait A: + def f = (i: Int) => i + +class B extends A: + def g = f \ No newline at end of file diff --git a/tests/pos/inline-trait-body-def-local-val.scala b/tests/pos/inline-trait-body-def-local-val.scala new file mode 100644 index 000000000000..c503432ec175 --- /dev/null +++ b/tests/pos/inline-trait-body-def-local-val.scala @@ -0,0 +1,7 @@ +inline trait A: + def f = + val foo = 1 + foo + +class B extends A: + def g = f \ No newline at end of file diff --git a/tests/pos/inline-trait-body-def-params.scala b/tests/pos/inline-trait-body-def-params.scala new file mode 100644 index 000000000000..79018db761a3 --- /dev/null +++ b/tests/pos/inline-trait-body-def-params.scala @@ -0,0 +1,7 @@ +inline trait A: + def x(foo: Int, bar: Unit) = + bar + foo + +class B extends A: + def f = x(1, ()) \ No newline at end of file diff --git a/tests/pos/inline-trait-body-def-parens.scala b/tests/pos/inline-trait-body-def-parens.scala new file mode 100644 index 000000000000..17c80e9d8ab7 --- /dev/null +++ b/tests/pos/inline-trait-body-def-parens.scala @@ -0,0 +1,5 @@ +inline trait A: + def x() = 1 + +class B extends A: + def f = x() \ No newline at end of file diff --git a/tests/pos/inline-trait-body-def-simple.scala b/tests/pos/inline-trait-body-def-simple.scala new file mode 100644 index 000000000000..edffe37168be --- /dev/null +++ b/tests/pos/inline-trait-body-def-simple.scala @@ -0,0 +1,5 @@ +inline trait A: + def x = 1 + +class B extends A: + def f = x \ No newline at end of file diff --git a/tests/pos/inline-trait-body-def-using.scala b/tests/pos/inline-trait-body-def-using.scala new file mode 100644 index 000000000000..1436962f6b48 --- /dev/null +++ b/tests/pos/inline-trait-body-def-using.scala @@ -0,0 +1,6 @@ +inline trait A: + given String = "AAA" + def foo(using s: String): String = s + s + +class B extends A: + def f = foo \ No newline at end of file diff --git a/tests/pos/inline-trait-body-lazy-val.scala b/tests/pos/inline-trait-body-lazy-val.scala new file mode 100644 index 000000000000..37ab5c249375 --- /dev/null +++ b/tests/pos/inline-trait-body-lazy-val.scala @@ -0,0 +1,5 @@ +inline trait A: + lazy val x = 1 + +class B extends A: + def f = x \ No newline at end of file diff --git a/tests/pos/inline-trait-body-macro-suspend/Macro.scala b/tests/pos/inline-trait-body-macro-suspend/Macro.scala new file mode 100644 index 000000000000..8aa252d8b7d0 --- /dev/null +++ b/tests/pos/inline-trait-body-macro-suspend/Macro.scala @@ -0,0 +1,10 @@ +import scala.quoted.* + +inline def foo(): Int = + ${fooImpl} + +def fooImpl(using Quotes): Expr[Int] = + '{3} + +inline trait A: + val i: Int = foo() diff --git a/tests/pos/inline-trait-body-macro-suspend/Test.scala b/tests/pos/inline-trait-body-macro-suspend/Test.scala new file mode 100644 index 000000000000..5079eecd497d --- /dev/null +++ b/tests/pos/inline-trait-body-macro-suspend/Test.scala @@ -0,0 +1,2 @@ +class B extends A: + def test = foo() \ No newline at end of file diff --git a/tests/pos/inline-trait-body-macro/Macro_1.scala b/tests/pos/inline-trait-body-macro/Macro_1.scala new file mode 100644 index 000000000000..8aa252d8b7d0 --- /dev/null +++ b/tests/pos/inline-trait-body-macro/Macro_1.scala @@ -0,0 +1,10 @@ +import scala.quoted.* + +inline def foo(): Int = + ${fooImpl} + +def fooImpl(using Quotes): Expr[Int] = + '{3} + +inline trait A: + val i: Int = foo() diff --git a/tests/pos/inline-trait-body-macro/Test_2.scala b/tests/pos/inline-trait-body-macro/Test_2.scala new file mode 100644 index 000000000000..26e47fd25fdb --- /dev/null +++ b/tests/pos/inline-trait-body-macro/Test_2.scala @@ -0,0 +1 @@ +class B extends A \ No newline at end of file diff --git a/tests/pos/inline-trait-body-rhs-type.scala b/tests/pos/inline-trait-body-rhs-type.scala new file mode 100644 index 000000000000..5b439ddf0c19 --- /dev/null +++ b/tests/pos/inline-trait-body-rhs-type.scala @@ -0,0 +1,6 @@ +inline trait A[T](x: T): + def f: T = x: T + def g: T = identity[T](x) + def h: this.type = this: this.type + +class B extends A("Hello") diff --git a/tests/pos/inline-trait-body-setter.scala b/tests/pos/inline-trait-body-setter.scala new file mode 100644 index 000000000000..f1fcf528da9f --- /dev/null +++ b/tests/pos/inline-trait-body-setter.scala @@ -0,0 +1,6 @@ +inline trait A: + def x = 1 + def x_= (x: Int) = ??? + var y = 1 + +class B extends A \ No newline at end of file diff --git a/tests/pos/inline-trait-body-trait-simple.scala b/tests/pos/inline-trait-body-trait-simple.scala new file mode 100644 index 000000000000..14d4bcf64563 --- /dev/null +++ b/tests/pos/inline-trait-body-trait-simple.scala @@ -0,0 +1,6 @@ +inline trait A[T]: + trait InnerA: + def x: T = ??? + +class B extends A[Int]: + class InnerB extends InnerA \ No newline at end of file diff --git a/tests/pos/inline-trait-body-type.scala b/tests/pos/inline-trait-body-type.scala new file mode 100644 index 000000000000..35cad5c81c99 --- /dev/null +++ b/tests/pos/inline-trait-body-type.scala @@ -0,0 +1,5 @@ +inline trait A[T]: + type U = String + +class B extends A[Int]: + def f: U = "ABD" \ No newline at end of file diff --git a/tests/pos/inline-trait-body-val-inline.scala b/tests/pos/inline-trait-body-val-inline.scala new file mode 100644 index 000000000000..b60a39cfb8fe --- /dev/null +++ b/tests/pos/inline-trait-body-val-inline.scala @@ -0,0 +1,5 @@ +inline trait A: + inline val x = 1 + +class B extends A: + def f = x \ No newline at end of file diff --git a/tests/pos/inline-trait-body-val.scala b/tests/pos/inline-trait-body-val.scala new file mode 100644 index 000000000000..be20419ffb9f --- /dev/null +++ b/tests/pos/inline-trait-body-val.scala @@ -0,0 +1,5 @@ +inline trait A: + val x = 1 + +class B extends A: + def f = x \ No newline at end of file diff --git a/tests/pos/inline-trait-inheritance-multiple-override.scala b/tests/pos/inline-trait-inheritance-multiple-override.scala new file mode 100644 index 000000000000..8a429deb5101 --- /dev/null +++ b/tests/pos/inline-trait-inheritance-multiple-override.scala @@ -0,0 +1,20 @@ +inline trait IT1: + def i: Int = 1 + def f[T](x: T): T = x + +inline trait IT2: + def j: String = "inline" + def g(x: Int): String = x.toString() + +trait T: + def k: List[Nothing] + def h(x: Int, y: Double): Double = x + y + +class C1 extends IT1, T: + override def i: Int = 123456 + def k = Nil + override def h(x: Int, y: Double): Double = 1.0 + +class C2 extends IT1, IT2: + override def i: Int = 567890 + override def f[T](x: T): T = ??? \ No newline at end of file diff --git a/tests/pos/inline-trait-inheritance-multiple.scala b/tests/pos/inline-trait-inheritance-multiple.scala new file mode 100644 index 000000000000..15013572f9b7 --- /dev/null +++ b/tests/pos/inline-trait-inheritance-multiple.scala @@ -0,0 +1,17 @@ +object InlineTraits: + inline trait IT1: + def i: Int = 1 + def f[T](x: T): T = x + + inline trait IT2: + def j: String = "inline" + def g(x: Int): String = x.toString() + +trait T: + def k: List[Nothing] + def h(x: Int, y: Double): Double = x + y + +class C1 extends InlineTraits.IT1, T: + def k = Nil + +class C2 extends InlineTraits.IT1, InlineTraits.IT2 \ No newline at end of file diff --git a/tests/pos/inline-trait-inheritance-single-abstract-class-override.scala b/tests/pos/inline-trait-inheritance-single-abstract-class-override.scala new file mode 100644 index 000000000000..7baaf3885d6b --- /dev/null +++ b/tests/pos/inline-trait-inheritance-single-abstract-class-override.scala @@ -0,0 +1,7 @@ +inline trait IT: + def i: Int = 1 + def f[T](x: T): T = x + +abstract class AC extends IT: + override def i: Int = 123456 + def j: Int diff --git a/tests/pos/inline-trait-inheritance-single-abstract-class.scala b/tests/pos/inline-trait-inheritance-single-abstract-class.scala new file mode 100644 index 000000000000..49243dfd56ec --- /dev/null +++ b/tests/pos/inline-trait-inheritance-single-abstract-class.scala @@ -0,0 +1,6 @@ +inline trait IT: + def i: Int = 1 + def f[T](x: T): T = x + +abstract class AC extends IT: + def j: Int diff --git a/tests/pos/inline-trait-inheritance-single-object-override.scala b/tests/pos/inline-trait-inheritance-single-object-override.scala new file mode 100644 index 000000000000..7e7726e0e8bb --- /dev/null +++ b/tests/pos/inline-trait-inheritance-single-object-override.scala @@ -0,0 +1,6 @@ +inline trait IT: + def i: Int = 1 + def f[T](x: T): T = x + +object O extends IT: + override def i: Int = 123456 diff --git a/tests/pos/inline-trait-inheritance-single-object.scala b/tests/pos/inline-trait-inheritance-single-object.scala new file mode 100644 index 000000000000..3916f2e3dc36 --- /dev/null +++ b/tests/pos/inline-trait-inheritance-single-object.scala @@ -0,0 +1,5 @@ +inline trait IT: + def i: Int = 1 + def f[T](x: T): T = x + +object O extends IT diff --git a/tests/pos/inline-trait-multiple-files/A.scala b/tests/pos/inline-trait-multiple-files/A.scala new file mode 100644 index 000000000000..a137403cdf53 --- /dev/null +++ b/tests/pos/inline-trait-multiple-files/A.scala @@ -0,0 +1,2 @@ +inline trait A: + val i: Int = 1 \ No newline at end of file diff --git a/tests/pos/inline-trait-multiple-files/B.scala b/tests/pos/inline-trait-multiple-files/B.scala new file mode 100644 index 000000000000..26e47fd25fdb --- /dev/null +++ b/tests/pos/inline-trait-multiple-files/B.scala @@ -0,0 +1 @@ +class B extends A \ No newline at end of file diff --git a/tests/pos/inline-trait-multiple-stages-defs/A_1.scala b/tests/pos/inline-trait-multiple-stages-defs/A_1.scala new file mode 100644 index 000000000000..97532bc38ae5 --- /dev/null +++ b/tests/pos/inline-trait-multiple-stages-defs/A_1.scala @@ -0,0 +1,10 @@ +inline trait A(x: Int): + def f: Int = 1 + def g(a: Int): Int = 2 + def h: Int + val i: Int = 3 + val j: Int + var k: Int = 4 + + inline val a = 5 + inline def b(a: Int): Int = 6 diff --git a/tests/pos/inline-trait-multiple-stages-defs/B_2.scala b/tests/pos/inline-trait-multiple-stages-defs/B_2.scala new file mode 100644 index 000000000000..b1a1d7d6af3d --- /dev/null +++ b/tests/pos/inline-trait-multiple-stages-defs/B_2.scala @@ -0,0 +1,3 @@ +class B extends A(10): + def h: Int = 11 + val j: Int = 12 diff --git a/tests/pos/inline-trait-multiple-stages-generic-defs/A_1.scala b/tests/pos/inline-trait-multiple-stages-generic-defs/A_1.scala new file mode 100644 index 000000000000..83e2f62dcd45 --- /dev/null +++ b/tests/pos/inline-trait-multiple-stages-generic-defs/A_1.scala @@ -0,0 +1,9 @@ +inline trait A[T](x: T): + def f: T = x + def g(a: T): T = a + def h: T + val i: T = x + val j: T + var k: T = x + + inline def b(a: T): T = x diff --git a/tests/pos/inline-trait-multiple-stages-generic-defs/B_2.scala b/tests/pos/inline-trait-multiple-stages-generic-defs/B_2.scala new file mode 100644 index 000000000000..b595626379b8 --- /dev/null +++ b/tests/pos/inline-trait-multiple-stages-generic-defs/B_2.scala @@ -0,0 +1,3 @@ +class B extends A[Int](10): + def h: Int = 11 + val j: Int = 12 diff --git a/tests/pos/inline-trait-multiple-stages/A_1.scala b/tests/pos/inline-trait-multiple-stages/A_1.scala new file mode 100644 index 000000000000..3596275f0498 --- /dev/null +++ b/tests/pos/inline-trait-multiple-stages/A_1.scala @@ -0,0 +1,2 @@ +inline trait A: + val i: Int = 1 diff --git a/tests/pos/inline-trait-multiple-stages/B_2.scala b/tests/pos/inline-trait-multiple-stages/B_2.scala new file mode 100644 index 000000000000..a18aec3dbe9b --- /dev/null +++ b/tests/pos/inline-trait-multiple-stages/B_2.scala @@ -0,0 +1 @@ +class B extends A diff --git a/tests/pos/inline-trait-signature-generic-context-bound.scala b/tests/pos/inline-trait-signature-generic-context-bound.scala new file mode 100644 index 000000000000..f3587911f168 --- /dev/null +++ b/tests/pos/inline-trait-signature-generic-context-bound.scala @@ -0,0 +1,4 @@ +inline trait A[T: List] + +given List[Int] = Nil +class B extends A \ No newline at end of file diff --git a/tests/pos/inline-trait-signature-generic-inferred-type.scala b/tests/pos/inline-trait-signature-generic-inferred-type.scala new file mode 100644 index 000000000000..275ed0c7ec5b --- /dev/null +++ b/tests/pos/inline-trait-signature-generic-inferred-type.scala @@ -0,0 +1,5 @@ +inline trait A[T](val x: T): + def f: T = x + +class B extends A(1): + val y: Int = f diff --git a/tests/pos/inline-trait-signature-generic-invariant.scala b/tests/pos/inline-trait-signature-generic-invariant.scala new file mode 100644 index 000000000000..cc20461e716e --- /dev/null +++ b/tests/pos/inline-trait-signature-generic-invariant.scala @@ -0,0 +1,4 @@ +inline trait A[T]: + def f(x: T): T = x + +class B extends A[Int] diff --git a/tests/pos/inline-trait-signature-generic-parameter.scala b/tests/pos/inline-trait-signature-generic-parameter.scala new file mode 100644 index 000000000000..174ceba06a8c --- /dev/null +++ b/tests/pos/inline-trait-signature-generic-parameter.scala @@ -0,0 +1,5 @@ +inline trait A[T](val x: T): + def f: T = x + +class B extends A[Int](1): + val y: Int = f diff --git a/tests/pos/inline-trait-signature-generic-refinement-type.scala b/tests/pos/inline-trait-signature-generic-refinement-type.scala new file mode 100644 index 000000000000..3683c55a4d02 --- /dev/null +++ b/tests/pos/inline-trait-signature-generic-refinement-type.scala @@ -0,0 +1,9 @@ +import reflect.Selectable.reflectiveSelectable + +class C[T](x: T): + def foo(): T = x + +inline trait A[T, U[T]](u: U[T]{ def foo(): T }): + def f: T = u.foo() + +class B extends A(C(1)) diff --git a/tests/pos/inline-trait-signature-generic-singleton.scala b/tests/pos/inline-trait-signature-generic-singleton.scala new file mode 100644 index 000000000000..c5d0cf2fcfb1 --- /dev/null +++ b/tests/pos/inline-trait-signature-generic-singleton.scala @@ -0,0 +1,4 @@ +inline trait A[T](x: T): + def f: T = x + +class B extends A[1](1) diff --git a/tests/pos/inline-trait-signature-generic-type-bounds.scala b/tests/pos/inline-trait-signature-generic-type-bounds.scala new file mode 100644 index 000000000000..9cf4a00c80d7 --- /dev/null +++ b/tests/pos/inline-trait-signature-generic-type-bounds.scala @@ -0,0 +1,4 @@ +inline trait A[T >: Int <: AnyVal]: + def f(x: T): T = x + +class B extends A[Int] diff --git a/tests/pos/inline-trait-signature-generic-variant.scala b/tests/pos/inline-trait-signature-generic-variant.scala new file mode 100644 index 000000000000..201a9254c47a --- /dev/null +++ b/tests/pos/inline-trait-signature-generic-variant.scala @@ -0,0 +1,8 @@ +inline trait Cov[+T]: + def f: T = ??? + +inline trait Contr[-T]: + def f(x: T) = ??? + +class A extends Cov[AnyVal] +class B extends Contr[Int] \ No newline at end of file diff --git a/tests/pos/inline-trait-signature-parameters-currying.scala b/tests/pos/inline-trait-signature-parameters-currying.scala new file mode 100644 index 000000000000..9d98e0596408 --- /dev/null +++ b/tests/pos/inline-trait-signature-parameters-currying.scala @@ -0,0 +1,4 @@ +inline trait A(i: Int)(j: Double): + def f: Double = i + j + +class B extends A(1)(1.0) diff --git a/tests/pos/inline-trait-signature-parameters-default-value.scala b/tests/pos/inline-trait-signature-parameters-default-value.scala new file mode 100644 index 000000000000..f76056ba70df --- /dev/null +++ b/tests/pos/inline-trait-signature-parameters-default-value.scala @@ -0,0 +1,4 @@ +inline trait A(val x: Int = 4) + +class B extends A(1) +class C extends A() \ No newline at end of file diff --git a/tests/pos/inline-trait-signature-parameters-implicit.scala b/tests/pos/inline-trait-signature-parameters-implicit.scala new file mode 100644 index 000000000000..df091c0cc7e6 --- /dev/null +++ b/tests/pos/inline-trait-signature-parameters-implicit.scala @@ -0,0 +1,4 @@ +inline trait A(implicit val imp: Int) + +implicit val x: Int = 1 +class B extends A \ No newline at end of file diff --git a/tests/pos/inline-trait-signature-parameters-using-nameless.scala b/tests/pos/inline-trait-signature-parameters-using-nameless.scala new file mode 100644 index 000000000000..1882b0348488 --- /dev/null +++ b/tests/pos/inline-trait-signature-parameters-using-nameless.scala @@ -0,0 +1,4 @@ +inline trait A(using Int) // error + +given x: Int = 1 +class B extends A \ No newline at end of file diff --git a/tests/pos/inline-trait-signature-parameters-using.scala b/tests/pos/inline-trait-signature-parameters-using.scala new file mode 100644 index 000000000000..5438359370f8 --- /dev/null +++ b/tests/pos/inline-trait-signature-parameters-using.scala @@ -0,0 +1,4 @@ +inline trait A(using usng: Int) + +given x: Int = 1 +class B extends A \ No newline at end of file diff --git a/tests/pos/inline-trait-signature-parameters-val-private.scala b/tests/pos/inline-trait-signature-parameters-val-private.scala new file mode 100644 index 000000000000..9306e0152f05 --- /dev/null +++ b/tests/pos/inline-trait-signature-parameters-val-private.scala @@ -0,0 +1,3 @@ +inline trait A(x: Int) + +class B extends A(1) \ No newline at end of file diff --git a/tests/pos/inline-trait-signature-parameters-val-protected.scala b/tests/pos/inline-trait-signature-parameters-val-protected.scala new file mode 100644 index 000000000000..4127d45e9002 --- /dev/null +++ b/tests/pos/inline-trait-signature-parameters-val-protected.scala @@ -0,0 +1,3 @@ +inline trait A(protected val x: Int) + +class B extends A(1) \ No newline at end of file diff --git a/tests/pos/inline-trait-signature-parameters-val.scala b/tests/pos/inline-trait-signature-parameters-val.scala new file mode 100644 index 000000000000..8372baa0b810 --- /dev/null +++ b/tests/pos/inline-trait-signature-parameters-val.scala @@ -0,0 +1,3 @@ +inline trait A(val x: Int) + +class B extends A(1) \ No newline at end of file diff --git a/tests/pos/inline-trait-signature-parameters-var.scala b/tests/pos/inline-trait-signature-parameters-var.scala new file mode 100644 index 000000000000..590d273f81b6 --- /dev/null +++ b/tests/pos/inline-trait-signature-parameters-var.scala @@ -0,0 +1,3 @@ +inline trait A(var x: Int) + +class B extends A(1) \ No newline at end of file diff --git a/tests/pos/inline-trait-signature-parentheses.scala b/tests/pos/inline-trait-signature-parentheses.scala new file mode 100644 index 000000000000..8e9c674edad6 --- /dev/null +++ b/tests/pos/inline-trait-signature-parentheses.scala @@ -0,0 +1,4 @@ +inline trait A(): + def i: Int = 1 + +class B extends A diff --git a/tests/pos/inline-trait-signature-sealed.scala b/tests/pos/inline-trait-signature-sealed.scala new file mode 100644 index 000000000000..db9d6a260ad3 --- /dev/null +++ b/tests/pos/inline-trait-signature-sealed.scala @@ -0,0 +1,4 @@ +inline sealed trait A: + final def f(x: Int) = x + +class B extends A \ No newline at end of file diff --git a/tests/pos/inline-trait-signature-simple.scala b/tests/pos/inline-trait-signature-simple.scala new file mode 100644 index 000000000000..6734a69a488c --- /dev/null +++ b/tests/pos/inline-trait-signature-simple.scala @@ -0,0 +1,4 @@ +inline trait A: + def i: Int = 1 + +class B extends A diff --git a/tests/pos/inline-trait-usage-anonymous-class.scala b/tests/pos/inline-trait-usage-anonymous-class.scala new file mode 100644 index 000000000000..c51dd05a6907 --- /dev/null +++ b/tests/pos/inline-trait-usage-anonymous-class.scala @@ -0,0 +1,4 @@ +inline trait A[T]: + val x: T = ??? + +val a = new A[Int] {} \ No newline at end of file diff --git a/tests/pos/inline-trait-usage-extension.scala b/tests/pos/inline-trait-usage-extension.scala new file mode 100644 index 000000000000..f9f5d87d2ceb --- /dev/null +++ b/tests/pos/inline-trait-usage-extension.scala @@ -0,0 +1,3 @@ +inline trait A + +class B extends A diff --git a/tests/pos/inline-trait-usage-inner.scala b/tests/pos/inline-trait-usage-inner.scala new file mode 100644 index 000000000000..3ed7869f0016 --- /dev/null +++ b/tests/pos/inline-trait-usage-inner.scala @@ -0,0 +1,5 @@ +object O: + inline trait A[T]: + def t: T = ??? + +class B extends O.A[Int] diff --git a/tests/pos/inline-trait-usage-param-type.scala b/tests/pos/inline-trait-usage-param-type.scala new file mode 100644 index 000000000000..e04f885f1e65 --- /dev/null +++ b/tests/pos/inline-trait-usage-param-type.scala @@ -0,0 +1,3 @@ +inline trait A + +def f(a: A) = ??? diff --git a/tests/pos/inline-trait-usage-return-type.scala b/tests/pos/inline-trait-usage-return-type.scala new file mode 100644 index 000000000000..e8015b065552 --- /dev/null +++ b/tests/pos/inline-trait-usage-return-type.scala @@ -0,0 +1,3 @@ +inline trait A + +def f: A = ??? diff --git a/tests/pos/inline-trait-usage-type-bound.scala b/tests/pos/inline-trait-usage-type-bound.scala new file mode 100644 index 000000000000..52b57f6acfa5 --- /dev/null +++ b/tests/pos/inline-trait-usage-type-bound.scala @@ -0,0 +1,3 @@ +inline trait A + +type T >: A <: A \ No newline at end of file diff --git a/tests/pos/inline-trait-usage-type.scala b/tests/pos/inline-trait-usage-type.scala new file mode 100644 index 000000000000..1c1ae110c367 --- /dev/null +++ b/tests/pos/inline-trait-usage-type.scala @@ -0,0 +1,3 @@ +inline trait A + +type T = List[A] \ No newline at end of file diff --git a/tests/run/inline-trait-body-lazy-val.scala b/tests/run/inline-trait-body-lazy-val.scala new file mode 100644 index 000000000000..68a9c9fd8697 --- /dev/null +++ b/tests/run/inline-trait-body-lazy-val.scala @@ -0,0 +1,9 @@ +inline trait A: + lazy val x = + throw new Exception + 1 + +class B extends A + +@main def Test: Unit = + val b = B() \ No newline at end of file diff --git a/tests/run/inline-trait-body-override-def.scala b/tests/run/inline-trait-body-override-def.scala new file mode 100644 index 000000000000..2ec53e02e669 --- /dev/null +++ b/tests/run/inline-trait-body-override-def.scala @@ -0,0 +1,9 @@ +inline trait A: + def foo: Unit = throw Exception("I should not be run!") + +class B extends A: + override def foo: Unit = () + +@main def Test = + val b = B() + b.foo diff --git a/tests/run/inline-trait-body-override-val.check b/tests/run/inline-trait-body-override-val.check new file mode 100644 index 000000000000..0cfbf08886fc --- /dev/null +++ b/tests/run/inline-trait-body-override-val.check @@ -0,0 +1 @@ +2 diff --git a/tests/run/inline-trait-body-override-val.scala b/tests/run/inline-trait-body-override-val.scala new file mode 100644 index 000000000000..ffd71ca0df56 --- /dev/null +++ b/tests/run/inline-trait-body-override-val.scala @@ -0,0 +1,9 @@ +inline trait A: + val x: Int = 1 + +class B extends A: + override val x = 2 + +@main def Test = + val b = B() + println(b.x) diff --git a/tests/run/inline-trait-body-statements.check b/tests/run/inline-trait-body-statements.check new file mode 100644 index 000000000000..73b256c35fd0 --- /dev/null +++ b/tests/run/inline-trait-body-statements.check @@ -0,0 +1,8 @@ +1 +foo +foo +1 +foo +bar +bar +bar diff --git a/tests/run/inline-trait-body-statements.scala b/tests/run/inline-trait-body-statements.scala new file mode 100644 index 000000000000..5da24022e99d --- /dev/null +++ b/tests/run/inline-trait-body-statements.scala @@ -0,0 +1,22 @@ +inline trait A[T]: + var x = 1 + def foo = + if x == 1 then println("1") + x = 1 - x + println("foo") + + foo + foo + foo + +class B extends A[Int]: + def bar = + if x == 1 then println("1") + println("bar") + + bar + bar + bar + +@main def Test = + B() \ No newline at end of file diff --git a/tests/run/inline-trait-body-var.check b/tests/run/inline-trait-body-var.check new file mode 100644 index 000000000000..94ebaf900161 --- /dev/null +++ b/tests/run/inline-trait-body-var.check @@ -0,0 +1,4 @@ +1 +2 +3 +4 diff --git a/tests/run/inline-trait-body-var.scala b/tests/run/inline-trait-body-var.scala new file mode 100644 index 000000000000..fda8fc3109b7 --- /dev/null +++ b/tests/run/inline-trait-body-var.scala @@ -0,0 +1,15 @@ +inline trait A: + var x = 1 + +class B extends A: + def f = + val old = x + x += 1 + old + +@main def Test = + val b = B() + println(b.f) + println(b.f) + println(b.f) + println(b.f) diff --git a/tests/run/inline-trait-inheritance-diamond-simple-trait.check b/tests/run/inline-trait-inheritance-diamond-simple-trait.check new file mode 100644 index 000000000000..adbdca39e3a4 --- /dev/null +++ b/tests/run/inline-trait-inheritance-diamond-simple-trait.check @@ -0,0 +1,4 @@ +1 +2 +999 +4 diff --git a/tests/run/inline-trait-inheritance-diamond-simple-trait.scala b/tests/run/inline-trait-inheritance-diamond-simple-trait.scala new file mode 100644 index 000000000000..0bb66f48b739 --- /dev/null +++ b/tests/run/inline-trait-inheritance-diamond-simple-trait.scala @@ -0,0 +1,22 @@ +trait TGP[T]: + def i: T + def f(x: T): T = x + +inline trait IT1[T](x: T) extends TGP[T]: + override def i: T = x + +inline trait IT2 extends TGP[Int]: + override def i: Int = 999 + def j: String = "inline" + def g(x: Int): String = x.toString() + +trait T extends TGP[Int] + +class C1 extends T, IT1[Int](1) +class C2 extends IT1[Int](2), T +class C3 extends IT1[Int](3), IT2 +class C4 extends IT2, IT1[Int](4) + +@main def Test: Unit = + for c <- List(C1(), C2(), C3(), C4()) + do println(c.i) \ No newline at end of file diff --git a/tests/run/inline-trait-inheritance-inline-ancestors/inlinetraits.scala b/tests/run/inline-trait-inheritance-inline-ancestors/inlinetraits.scala new file mode 100644 index 000000000000..e695ef3efd1a --- /dev/null +++ b/tests/run/inline-trait-inheritance-inline-ancestors/inlinetraits.scala @@ -0,0 +1,47 @@ +package inlinetraits + +val inlineValues: List[Int] = + val c = C() + List(c.zero, c.eleven, c.twelve, c.thirteen, c.twentyOne, c.twentyTwo, c.thirty) + +inline trait T0: + def zero: Int = 0 + def eleven: Int = 0 + def twelve: Int = 0 + def twentyOne: Int = 0 + def thirteen: Int = 0 + def twentyTwo: Int = 0 + def thirty: Int = 0 + +inline trait T11 extends T0: + override def eleven: Int = 11 + override def twelve: Int = 11 + override def twentyOne: Int = 11 + override def thirteen: Int = 11 + override def twentyTwo: Int = 11 + override def thirty: Int = 11 + +inline trait T12 extends T0: + override def twelve: Int = 12 + override def twentyOne: Int = 12 + override def thirteen: Int = 12 + override def twentyTwo: Int = 12 + override def thirty: Int = 12 + +inline trait T21 extends T11, T12: + override def twentyOne: Int = 21 + override def thirteen: Int = 21 + override def twentyTwo: Int = 21 + override def thirty: Int = 21 + +inline trait T13 extends T0: + override def thirteen: Int = 13 + override def twentyTwo: Int = 13 + override def thirty: Int = 13 + +inline trait T22 extends T12, T13: + override def twentyTwo: Int = 22 + override def thirty: Int = 22 + +class C extends T21, T22: + override def thirty: Int = 30 diff --git a/tests/run/inline-trait-inheritance-inline-ancestors/normaltraits.scala b/tests/run/inline-trait-inheritance-inline-ancestors/normaltraits.scala new file mode 100644 index 000000000000..ba128145c793 --- /dev/null +++ b/tests/run/inline-trait-inheritance-inline-ancestors/normaltraits.scala @@ -0,0 +1,47 @@ +package normaltraits + +val normalValues: List[Int] = + val c = C() + List(c.zero, c.eleven, c.twelve, c.thirteen, c.twentyOne, c.twentyTwo, c.thirty) + +trait T0: + def zero: Int = 0 + def eleven: Int = 0 + def twelve: Int = 0 + def twentyOne: Int = 0 + def thirteen: Int = 0 + def twentyTwo: Int = 0 + def thirty: Int = 0 + +trait T11 extends T0: + override def eleven: Int = 11 + override def twelve: Int = 11 + override def twentyOne: Int = 11 + override def thirteen: Int = 11 + override def twentyTwo: Int = 11 + override def thirty: Int = 11 + +trait T12 extends T0: + override def twelve: Int = 12 + override def twentyOne: Int = 12 + override def thirteen: Int = 12 + override def twentyTwo: Int = 12 + override def thirty: Int = 12 + +trait T21 extends T11, T12: + override def twentyOne: Int = 21 + override def thirteen: Int = 21 + override def twentyTwo: Int = 21 + override def thirty: Int = 21 + +trait T13 extends T0: + override def thirteen: Int = 13 + override def twentyTwo: Int = 13 + override def thirty: Int = 13 + +trait T22 extends T12, T13: + override def twentyTwo: Int = 22 + override def thirty: Int = 22 + +class C extends T21, T22: + override def thirty: Int = 30 \ No newline at end of file diff --git a/tests/run/inline-trait-inheritance-inline-ancestors/test.scala b/tests/run/inline-trait-inheritance-inline-ancestors/test.scala new file mode 100644 index 000000000000..5f7a9be7a41b --- /dev/null +++ b/tests/run/inline-trait-inheritance-inline-ancestors/test.scala @@ -0,0 +1,5 @@ +import normaltraits.normalValues +import inlinetraits.inlineValues + +@main def Test = + assert(normalValues == inlineValues) \ No newline at end of file diff --git a/tests/run/inline-trait-inheritance-inline-grandparent.check b/tests/run/inline-trait-inheritance-inline-grandparent.check new file mode 100644 index 000000000000..02a3fcae5b4e --- /dev/null +++ b/tests/run/inline-trait-inheritance-inline-grandparent.check @@ -0,0 +1,6 @@ +0 +(Test SimpleC,Hello) + +5678 +(Test C,Hello,9) +5678 diff --git a/tests/run/inline-trait-inheritance-inline-grandparent.scala b/tests/run/inline-trait-inheritance-inline-grandparent.scala new file mode 100644 index 000000000000..adc00b7b7bfe --- /dev/null +++ b/tests/run/inline-trait-inheritance-inline-grandparent.scala @@ -0,0 +1,33 @@ +package simpleGrandParent: + inline trait SimpleGrandParent[T]: + def foo(): Int = 0 + def foooo(): T = ??? + + inline trait SimpleParent[T, U](x: T, z: U) extends SimpleGrandParent[U]: + def bar(a: T) = (a, x) + + class SimpleC extends SimpleParent("Hello", 1234) + +package grandParentWithArgs: + inline trait GrandParent[T](val x: T, val y: T): + def foo(): T = x + def foooo(): T = y + + inline trait Parent[T, U](x: T, z: U) extends GrandParent[U]: + def bar(a: T) = (a, x, y) + + class C extends Parent("Hello", 1234), GrandParent(5678, 9) + +@main def Test = + import simpleGrandParent.SimpleC + import grandParentWithArgs.C + + val simpleC = SimpleC() + println(simpleC.foo()) + println(simpleC.bar("Test SimpleC")) + println + + val c = C() + println(c.foo()) + println(c.bar("Test C")) + println(c.x) \ No newline at end of file diff --git a/tests/run/inline-trait-signature-parameters-val-block.check b/tests/run/inline-trait-signature-parameters-val-block.check new file mode 100644 index 000000000000..063724ea6c43 --- /dev/null +++ b/tests/run/inline-trait-signature-parameters-val-block.check @@ -0,0 +1,4 @@ +I am a B! +1 +1 +1 diff --git a/tests/run/inline-trait-signature-parameters-val-block.scala b/tests/run/inline-trait-signature-parameters-val-block.scala new file mode 100644 index 000000000000..794a6071d073 --- /dev/null +++ b/tests/run/inline-trait-signature-parameters-val-block.scala @@ -0,0 +1,10 @@ +inline trait A(val x: Int) + +class B extends A({ println("I am a B!"); 1 }) + +@main() def Test: Unit = { + val b = B() + println(b.x) + println(b.x) + println(b.x) +} \ No newline at end of file diff --git a/tests/run/inline-trait-signature-side-effects-1.check b/tests/run/inline-trait-signature-side-effects-1.check new file mode 100644 index 000000000000..4df3c9a85d7f --- /dev/null +++ b/tests/run/inline-trait-signature-side-effects-1.check @@ -0,0 +1,6 @@ +0 +0 +1 +0 +1 +2 diff --git a/tests/run/inline-trait-signature-side-effects-1.scala b/tests/run/inline-trait-signature-side-effects-1.scala new file mode 100644 index 000000000000..61c348eb48d1 --- /dev/null +++ b/tests/run/inline-trait-signature-side-effects-1.scala @@ -0,0 +1,27 @@ +inline trait A(i: Int): + val x = i + val y = i + +class B(i: Int) extends A(i) + +@main def Test = + var c = 0 + + val b1 = new B({ + for i <- 0 to c + do println(i) + c += 1 + c + }) + val b2 = new B({ + for i <- 0 to c + do println(i) + c += 1 + c + }) + val b3 = new B({ + for i <- 0 to c + do println(i) + c += 1 + c + }) \ No newline at end of file diff --git a/tests/run/inline-trait-signature-side-effects-2.check b/tests/run/inline-trait-signature-side-effects-2.check new file mode 100644 index 000000000000..4df3c9a85d7f --- /dev/null +++ b/tests/run/inline-trait-signature-side-effects-2.check @@ -0,0 +1,6 @@ +0 +0 +1 +0 +1 +2 diff --git a/tests/run/inline-trait-signature-side-effects-2.scala b/tests/run/inline-trait-signature-side-effects-2.scala new file mode 100644 index 000000000000..19dcad0681a3 --- /dev/null +++ b/tests/run/inline-trait-signature-side-effects-2.scala @@ -0,0 +1,16 @@ +inline trait A(i: Int): + val x = i + val y = i + +class B(i: Int) extends A(i) + +@main def Test = + var c = 0 + + for _ <- 0 until 3 + do new B({ + for i <- 0 to c + do println(i) + c += 1 + c + }) \ No newline at end of file