diff --git a/community-build/community-projects/scodec b/community-build/community-projects/scodec index 96a77ecaaf91..b74f2085f071 160000 --- a/community-build/community-projects/scodec +++ b/community-build/community-projects/scodec @@ -1 +1 @@ -Subproject commit 96a77ecaaf913f195bb4079966a2e9fb41ce214e +Subproject commit b74f2085f07165d84b32c39eb214c9cc838711cc diff --git a/community-build/community-projects/shapeless-3 b/community-build/community-projects/shapeless-3 index d27c5ba1ae51..a59fb8e24240 160000 --- a/community-build/community-projects/shapeless-3 +++ b/community-build/community-projects/shapeless-3 @@ -1 +1 @@ -Subproject commit d27c5ba1ae5111b85df2cfb65a26b9246c52570c +Subproject commit a59fb8e242409c3f51d5235247dfa0deeb1bb890 diff --git a/compiler/src/dotty/tools/dotc/printing/Formatting.scala b/compiler/src/dotty/tools/dotc/printing/Formatting.scala index ac13f0161c70..6f1c32beb822 100644 --- a/compiler/src/dotty/tools/dotc/printing/Formatting.scala +++ b/compiler/src/dotty/tools/dotc/printing/Formatting.scala @@ -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 diff --git a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala index 33f5dcf1b1f5..e51f0a8b77ac 100644 --- a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala +++ b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala @@ -208,6 +208,7 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe case UnstableInlineAccessorID // errorNumber: 192 case VolatileOnValID // errorNumber: 193 case ExtensionNullifiedByMemberID // errorNumber: 194 + case InlinedAnonClassWarningID // errorNumber: 195 def errorNumber = ordinal - 1 diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 2381bd80babc..ebc9ad2a646c 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -3104,6 +3104,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 diff --git a/compiler/src/dotty/tools/dotc/transform/PruneErasedDefs.scala b/compiler/src/dotty/tools/dotc/transform/PruneErasedDefs.scala index 9bb30926d45a..70123ddd77a3 100644 --- a/compiler/src/dotty/tools/dotc/transform/PruneErasedDefs.scala +++ b/compiler/src/dotty/tools/dotc/transform/PruneErasedDefs.scala @@ -10,6 +10,7 @@ import Symbols.* import typer.RefChecks import MegaPhase.MiniPhase import ast.tpd +import reporting.InlinedAnonClassWarning import config.Feature import Decorators.* @@ -51,6 +52,7 @@ class PruneErasedDefs extends MiniPhase with SymTransformer { thisTransform => else cpy.ValDef(tree)(rhs = trivialErasedTree(tree.rhs)) override def transformDefDef(tree: DefDef)(using Context): Tree = + RefChecks.checkNoInlineAnnoClasses(tree) checkErasedInExperimental(tree.symbol) if !tree.symbol.isEffectivelyErased || tree.rhs.isEmpty then tree else cpy.DefDef(tree)(rhs = trivialErasedTree(tree.rhs)) diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index cdfd137e5661..e075405b5035 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -33,6 +33,16 @@ object RefChecks { def isStable = true } + 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) + /** Only one overloaded alternative is allowed to define default arguments */ private def checkOverloadedRestrictions(clazz: Symbol)(using Context): Unit = { // Using the default getters (such as methodName$default$1) as a cheap way of diff --git a/tests/neg/i13044.check b/tests/neg/i13044.check index 1fbe978a49b8..55be9b6b08df 100644 --- a/tests/neg/i13044.check +++ b/tests/neg/i13044.check @@ -1,3 +1,15 @@ +-- [E195] Potential Issue Warning: tests/neg/i13044.scala:26:8 --------------------------------------------------------- +26 | new Schema[A] { + | ^ + | New anonymous class definition will be duplicated at each inline site + | + | longer explanation available when compiling with `-explain` +-- [E195] Potential Issue Warning: tests/neg/i13044.scala:32:8 --------------------------------------------------------- +32 | new Schema[A] { + | ^ + | New anonymous class definition will be duplicated at each inline site + | + | longer explanation available when compiling with `-explain` -- Error: tests/neg/i13044.scala:65:40 --------------------------------------------------------------------------------- 65 | implicit def typeSchema: Schema[A] = Schema.gen // error // error | ^^^^^^^^^^ diff --git a/tests/pos/i17314.scala b/tests/pos/i17314.scala index 2d0c409ced10..8ece4a3bd7ac 100644 --- a/tests/pos/i17314.scala +++ b/tests/pos/i17314.scala @@ -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 { diff --git a/tests/pos/not-looping-implicit.scala b/tests/pos/not-looping-implicit.scala index 90fba9f807a7..ebaf25e760f2 100644 --- a/tests/pos/not-looping-implicit.scala +++ b/tests/pos/not-looping-implicit.scala @@ -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] diff --git a/tests/run/i11050.scala b/tests/run/i11050.scala index 90a6ec84df85..027812c013c4 100644 --- a/tests/run/i11050.scala +++ b/tests/run/i11050.scala @@ -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) diff --git a/tests/warn/i15503i.scala b/tests/warn/i15503i.scala index 329b81327288..f4d41c53d981 100644 --- a/tests/warn/i15503i.scala +++ b/tests/warn/i15503i.scala @@ -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: @@ -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: @@ -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 diff --git a/tests/warn/i15503j.scala b/tests/warn/i15503j.scala index f5e15bb79f79..fa30601d8960 100644 --- a/tests/warn/i15503j.scala +++ b/tests/warn/i15503j.scala @@ -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] \ No newline at end of file diff --git a/tests/warn/i16723.check b/tests/warn/i16723.check new file mode 100644 index 000000000000..ed8e55502a80 --- /dev/null +++ b/tests/warn/i16723.check @@ -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` diff --git a/tests/warn/i16723.scala b/tests/warn/i16723.scala new file mode 100644 index 000000000000..32875f4edf36 --- /dev/null +++ b/tests/warn/i16723.scala @@ -0,0 +1,3 @@ +inline def foo = + class NotAnon + new Object {} // warn \ No newline at end of file diff --git a/tests/warn/i16723a.check b/tests/warn/i16723a.check new file mode 100644 index 000000000000..ba4794fac23e --- /dev/null +++ b/tests/warn/i16723a.check @@ -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` diff --git a/tests/warn/i16723a.scala b/tests/warn/i16723a.scala new file mode 100644 index 000000000000..90aaeb868d4e --- /dev/null +++ b/tests/warn/i16723a.scala @@ -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 \ No newline at end of file