Skip to content

Commit

Permalink
Backport "Add warning for anonymous inline classes (#16723)" to LTS (#…
Browse files Browse the repository at this point in the history
…21110)

Backports #20291 to the LTS branch.

PR submitted by the release tooling.
  • Loading branch information
WojciechMazur committed Jul 8, 2024
2 parents 0d787e2 + 7a6b83b commit ffaab87
Show file tree
Hide file tree
Showing 19 changed files with 119 additions and 47 deletions.
2 changes: 1 addition & 1 deletion community-build/community-projects/scodec
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,10 @@ object projects:

lazy val shapeless3 = SbtCommunityProject(
project = "shapeless-3",
sbtTestCommand = "testJVM; testJS",
sbtTestCommand = List(
"""set derivingJVM/scalacOptions += "-Wconf:msg=New anonymous class definition will be duplicated at each inline site:s" """,
"""set derivingJS/scalacOptions += "-Wconf:msg=New anonymous class definition will be duplicated at each inline site:s" """,
"testJVM", "testJS").mkString("; "),
sbtDocCommand = forceDoc("typeable", "deriving"),
scalacOptions = SbtCommunityProject.scalacOptions.filter(_ != "-Ysafe-init"), // due to -Xfatal-warnings
)
Expand Down
4 changes: 3 additions & 1 deletion compiler/src/dotty/tools/dotc/printing/Formatting.scala
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ object Formatting {
trait CtxShow:
def run(using Context): Shown

private inline def CtxShow(inline x: Context ?=> Shown) = new CtxShow { def run(using Context) = x(using ctx) }
private inline def CtxShow(inline x: Context ?=> Shown) =
class InlinedCtxShow extends CtxShow { def run(using Context) = x(using ctx) }
new InlinedCtxShow
private def toStr[A: Show](x: A)(using Context): String = Shown.toStr(toShown(x))
private def toShown[A: Show](x: A)(using Context): Shown = Show[A].show(x).runCtxShow

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe
case UnstableInlineAccessorID // errorNumber: 192 - unused in LTS
case VolatileOnValID // errorNumber: 193
case ExtensionNullifiedByMemberID // errorNumber: 194
case InlinedAnonClassWarningID // errorNumber: 195

def errorNumber = ordinal - 1

Expand Down
9 changes: 9 additions & 0 deletions compiler/src/dotty/tools/dotc/reporting/messages.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3073,6 +3073,15 @@ extends SyntaxMsg(InlineGivenShouldNotBeFunctionID):
| inline def apply(x: A) = x.toB
"""

class InlinedAnonClassWarning()(using Context)
extends Message(InlinedAnonClassWarningID):
def kind = MessageKind.PotentialIssue
def msg(using Context) = "New anonymous class definition will be duplicated at each inline site"
def explain(using Context) =
i"""Anonymous class will be defined at each use site, which may lead to a larger number of classfiles.
|
|To inline class definitions, you may provide an explicit class name to avoid this warning."""

class ValueDiscarding(tp: Type)(using Context)
extends Message(ValueDiscardingID):
def kind = MessageKind.PotentialIssue
Expand Down
12 changes: 12 additions & 0 deletions compiler/src/dotty/tools/dotc/transform/PruneErasedDefs.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import Symbols.*
import typer.RefChecks
import MegaPhase.MiniPhase
import ast.tpd
import reporting.InlinedAnonClassWarning

import config.Feature
import Decorators.*
Expand Down Expand Up @@ -51,6 +52,17 @@ class PruneErasedDefs extends MiniPhase with SymTransformer { thisTransform =>
else cpy.ValDef(tree)(rhs = trivialErasedTree(tree.rhs))

override def transformDefDef(tree: DefDef)(using Context): Tree =
def checkNoInlineAnnoClasses(tree: DefDef)(using Context): Unit =
if tree.symbol.is(Inline) then
new TreeTraverser {
def traverse(tree: Tree)(using Context): Unit =
tree match
case tree: TypeDef if tree.symbol.isAnonymousClass =>
report.warning(new InlinedAnonClassWarning(), tree.symbol.sourcePos)
case _ => traverseChildren(tree)
}.traverse(tree)

checkNoInlineAnnoClasses(tree)
checkErasedInExperimental(tree.symbol)
if !tree.symbol.isEffectivelyErased || tree.rhs.isEmpty then tree
else cpy.DefDef(tree)(rhs = trivialErasedTree(tree.rhs))
Expand Down
48 changes: 24 additions & 24 deletions tests/neg/i13044.check
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
-- Error: tests/neg/i13044.scala:50:40 ---------------------------------------------------------------------------------
50 | implicit def typeSchema: Schema[A] = Schema.gen // error // error
-- Error: tests/neg/i13044.scala:46:40 ---------------------------------------------------------------------------------
46 | implicit def typeSchema: Schema[A] = Schema.gen // error // error
| ^^^^^^^^^^
| given instance gen is declared as `inline`, but was not inlined
|
Expand All @@ -12,47 +12,47 @@
| ^
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|This location contains code that was inlined from i13044.scala:17
31 | lazy val fields = recurse[m.MirroredElemTypes]
29 | lazy val fields = recurse[m.MirroredElemTypes]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|This location contains code that was inlined from i13044.scala:17
37 | inline given gen[A]: Schema[A] = derived
33 | inline given gen[A]: Schema[A] = derived
| ^^^^^^^
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|This location contains code that was inlined from i13044.scala:17
17 | val builder = summonInline[Schema[t]].asInstanceOf[Schema[Any]]
| ^
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|This location contains code that was inlined from i13044.scala:17
31 | lazy val fields = recurse[m.MirroredElemTypes]
29 | lazy val fields = recurse[m.MirroredElemTypes]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|This location contains code that was inlined from i13044.scala:17
37 | inline given gen[A]: Schema[A] = derived
33 | inline given gen[A]: Schema[A] = derived
| ^^^^^^^
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|This location contains code that was inlined from i13044.scala:17
17 | val builder = summonInline[Schema[t]].asInstanceOf[Schema[Any]]
| ^
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|This location contains code that was inlined from i13044.scala:17
31 | lazy val fields = recurse[m.MirroredElemTypes]
29 | lazy val fields = recurse[m.MirroredElemTypes]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|This location contains code that was inlined from i13044.scala:17
37 | inline given gen[A]: Schema[A] = derived
33 | inline given gen[A]: Schema[A] = derived
| ^^^^^^^
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|This location contains code that was inlined from i13044.scala:17
17 | val builder = summonInline[Schema[t]].asInstanceOf[Schema[Any]]
| ^
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|This location contains code that was inlined from i13044.scala:17
31 | lazy val fields = recurse[m.MirroredElemTypes]
29 | lazy val fields = recurse[m.MirroredElemTypes]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|This location contains code that was inlined from i13044.scala:17
37 | inline given gen[A]: Schema[A] = derived
33 | inline given gen[A]: Schema[A] = derived
| ^^^^^^^
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|This location contains code that was inlined from i13044.scala:17
Expand All @@ -64,15 +64,15 @@
| ^^^^^^^^^^^
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|This location contains code that was inlined from i13044.scala:17
31 | lazy val fields = recurse[m.MirroredElemTypes]
29 | lazy val fields = recurse[m.MirroredElemTypes]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|This location contains code that was inlined from i13044.scala:17
37 | inline given gen[A]: Schema[A] = derived
33 | inline given gen[A]: Schema[A] = derived
| ^^^^^^^
--------------------------------------------------------------------------------------------------------------------
-- Error: tests/neg/i13044.scala:50:40 ---------------------------------------------------------------------------------
50 | implicit def typeSchema: Schema[A] = Schema.gen // error // error
-- Error: tests/neg/i13044.scala:46:40 ---------------------------------------------------------------------------------
46 | implicit def typeSchema: Schema[A] = Schema.gen // error // error
| ^^^^^^^^^^
| method recurse is declared as `inline`, but was not inlined
|
Expand All @@ -85,47 +85,47 @@
| ^^^^^^^
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|This location contains code that was inlined from i13044.scala:18
31 | lazy val fields = recurse[m.MirroredElemTypes]
29 | lazy val fields = recurse[m.MirroredElemTypes]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|This location contains code that was inlined from i13044.scala:18
37 | inline given gen[A]: Schema[A] = derived
33 | inline given gen[A]: Schema[A] = derived
| ^^^^^^^
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|This location contains code that was inlined from i13044.scala:18
17 | val builder = summonInline[Schema[t]].asInstanceOf[Schema[Any]]
| ^
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|This location contains code that was inlined from i13044.scala:18
31 | lazy val fields = recurse[m.MirroredElemTypes]
29 | lazy val fields = recurse[m.MirroredElemTypes]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|This location contains code that was inlined from i13044.scala:18
37 | inline given gen[A]: Schema[A] = derived
33 | inline given gen[A]: Schema[A] = derived
| ^^^^^^^
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|This location contains code that was inlined from i13044.scala:18
17 | val builder = summonInline[Schema[t]].asInstanceOf[Schema[Any]]
| ^
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|This location contains code that was inlined from i13044.scala:18
31 | lazy val fields = recurse[m.MirroredElemTypes]
29 | lazy val fields = recurse[m.MirroredElemTypes]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|This location contains code that was inlined from i13044.scala:18
37 | inline given gen[A]: Schema[A] = derived
33 | inline given gen[A]: Schema[A] = derived
| ^^^^^^^
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|This location contains code that was inlined from i13044.scala:18
17 | val builder = summonInline[Schema[t]].asInstanceOf[Schema[Any]]
| ^
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|This location contains code that was inlined from i13044.scala:18
31 | lazy val fields = recurse[m.MirroredElemTypes]
29 | lazy val fields = recurse[m.MirroredElemTypes]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|This location contains code that was inlined from i13044.scala:18
37 | inline given gen[A]: Schema[A] = derived
33 | inline given gen[A]: Schema[A] = derived
| ^^^^^^^
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|This location contains code that was inlined from i13044.scala:18
Expand All @@ -137,10 +137,10 @@
| ^^^^^^^^^^^
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|This location contains code that was inlined from i13044.scala:18
31 | lazy val fields = recurse[m.MirroredElemTypes]
29 | lazy val fields = recurse[m.MirroredElemTypes]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|This location contains code that was inlined from i13044.scala:18
37 | inline given gen[A]: Schema[A] = derived
33 | inline given gen[A]: Schema[A] = derived
| ^^^^^^^
--------------------------------------------------------------------------------------------------------------------
8 changes: 2 additions & 6 deletions tests/neg/i13044.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,11 @@ trait SchemaDerivation {
inline summonInline[Mirror.Of[A]] match {
case m: Mirror.SumOf[A] =>
lazy val subTypes = recurse[m.MirroredElemTypes]
new Schema[A] {
def build: A = ???
}
???

case m: Mirror.ProductOf[A] =>
lazy val fields = recurse[m.MirroredElemTypes]
new Schema[A] {
def build: A = ???
}
???
}

inline given gen[A]: Schema[A] = derived
Expand Down
3 changes: 2 additions & 1 deletion tests/pos/i17314.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ object circelike {
inline final def derived[A](using conf: Configuration)(using
inline mirror: Mirror.Of[A]
): ConfiguredCodec[A] =
new ConfiguredCodec[A]:
class InlinedConfiguredCodec extends ConfiguredCodec[A]:
val codec = summonInline[Codec[URI]] // simplification
new InlinedConfiguredCodec
}

object foo {
Expand Down
4 changes: 2 additions & 2 deletions tests/pos/not-looping-implicit.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ object Schema {
inline summonInline[Mirror.Of[A]] match {
case m: Mirror.SumOf[A] =>
lazy val members = recurse[m.MirroredElemLabels, m.MirroredElemTypes]()
new Schema[A] {}
???
case m: Mirror.ProductOf[A] =>
lazy val fields = recurse[m.MirroredElemLabels, m.MirroredElemTypes]()
new Schema[A] {}
???
}

inline given gen[A]: Schema[A] = derived[A]
Expand Down
12 changes: 7 additions & 5 deletions tests/run/i11050.scala
Original file line number Diff line number Diff line change
Expand Up @@ -113,12 +113,14 @@ object Show:

inline def show[T](x: T): String = summonInline[Show[T]].show(x)

transparent inline def derived[T](implicit ev: Mirror.Of[T]): Show[T] = new {
def show(x: T): String = inline ev match {
case m: Mirror.ProductOf[T] => showProduct(x.asInstanceOf[Product], m)
case m: Mirror.SumOf[T] => showCases[m.MirroredElemTypes](0)(x, m.ordinal(x))
transparent inline def derived[T](implicit ev: Mirror.Of[T]): Show[T] =
class InlinedShow extends Show[T] { // provide name to anonymous class
def show(x: T): String = inline ev match {
case m: Mirror.ProductOf[T] => showProduct(x.asInstanceOf[Product], m)
case m: Mirror.SumOf[T] => showCases[m.MirroredElemTypes](0)(x, m.ordinal(x))
}
}
}
new InlinedShow

transparent inline def showProduct[T](x: Product, m: Mirror.ProductOf[T]): String =
constValue[m.MirroredLabel] + showElems[m.MirroredElemTypes, m.MirroredElemLabels](0, Nil)(x)
Expand Down
6 changes: 3 additions & 3 deletions tests/warn/i15503i.scala
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ package foo.test.i16679a:
import scala.deriving.Mirror
object CaseClassByStringName:
inline final def derived[A](using inline A: Mirror.Of[A]): CaseClassByStringName[A] =
new CaseClassByStringName[A]:
new CaseClassByStringName[A]: // warn
def name: String = A.toString

object secondPackage:
Expand All @@ -263,7 +263,7 @@ package foo.test.i16679b:
object CaseClassName:
import scala.deriving.Mirror
inline final def derived[A](using inline A: Mirror.Of[A]): CaseClassName[A] =
new CaseClassName[A]:
new CaseClassName[A]: // warn
def name: String = A.toString

object Foo:
Expand All @@ -279,7 +279,7 @@ package foo.test.i17156:
package a:
trait Foo[A]
object Foo:
inline def derived[T]: Foo[T] = new Foo{}
inline def derived[T]: Foo[T] = new Foo{} // warn

package b:
import a.Foo
Expand Down
4 changes: 2 additions & 2 deletions tests/warn/i15503j.scala
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,11 @@ package foo.unused.summon.inlines:

transparent inline given conflictInside: C =
summonInline[A]
new {}
???

transparent inline given potentialConflict: C =
summonInline[B]
new {}
???

val b: B = summon[B]
val c: C = summon[C]
6 changes: 6 additions & 0 deletions tests/warn/i16723.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-- [E195] Potential Issue Warning: tests/warn/i16723.scala:3:2 ---------------------------------------------------------
3 | new Object {} // warn
| ^
| New anonymous class definition will be duplicated at each inline site
|
| longer explanation available when compiling with `-explain`
3 changes: 3 additions & 0 deletions tests/warn/i16723.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
inline def foo =
class NotAnon
new Object {} // warn
6 changes: 6 additions & 0 deletions tests/warn/i16723a.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-- [E195] Potential Issue Warning: tests/warn/i16723a.scala:5:38 -------------------------------------------------------
5 |inline given Converter[Int, String] = new Converter { // warn
| ^
| New anonymous class definition will be duplicated at each inline site
|
| longer explanation available when compiling with `-explain`
17 changes: 17 additions & 0 deletions tests/warn/i16723a.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
trait Converter[A, B] {
def convert: A => B
}

inline given Converter[Int, String] = new Converter { // warn
def convert = _.toString()
}

def foo(using bar: Converter[Int, String]) =
"foo"

@main
def main =
foo
foo
foo
foo
14 changes: 14 additions & 0 deletions tests/warn/i16723b.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// LTS specifc, not applicable to Next thanks to #17548 improvements
trait Empty[T]:
def empty: T

object Empty:
inline def withOnly[F[_], R, T](f: [t <: T] => F[t] => R): R = f(null.asInstanceOf[F[T]])

def mkEmpty[T](t: T): Empty[T] = ???

inline given emptyGenC[A]: Empty[A] =
mkEmpty(withOnly[Empty, A, A]([a <: A] => (_: Empty[a]).empty)) // warn

@main def Test =
Empty.emptyGenC[String]

0 comments on commit ffaab87

Please sign in to comment.