From 9b3cdc206415e91d0143832ef98c82476f9ad45a Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 17 Jan 2022 14:39:35 +0100 Subject: [PATCH 1/5] Properly desugar `inline given .. with ..` ```scala inline given Foo with { .. } ``` now becomes ```scala inline given def given_Foo: Foo with {..} = new Foo class given_Foo extends Foo with { } ``` There are no creation of `lazy vals` when we have `inline`. We inline the `new givne_Foo` only, the class does not get inlined. This is useful to remove the instatiation of the given instance when the methods are inline themselfs. Fixes #14282 Fixes #14177 --- compiler/src/dotty/tools/dotc/ast/Desugar.scala | 4 ++-- .../src/dotty/tools/dotc/parsing/Parsers.scala | 2 +- tests/neg/i14177a.scala | 6 ++++++ tests/pos/i14177b.scala | 15 +++++++++++++++ tests/pos/i14282.scala | 13 +++++++++++++ 5 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 tests/neg/i14177a.scala create mode 100644 tests/pos/i14177b.scala create mode 100644 tests/pos/i14282.scala diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 7121f7e7370d..75e300118562 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -783,7 +783,7 @@ object desugar { DefDef( className.toTermName, joinParams(constrTparams, defParamss), classTypeRef, creatorExpr) - .withMods(companionMods | mods.flags.toTermFlags & GivenOrImplicit | Final) + .withMods(companionMods | mods.flags.toTermFlags & (GivenOrImplicit | Inline) | Final) .withSpan(cdef.span) :: Nil } @@ -809,7 +809,7 @@ object desugar { Nil } } - val classMods = if mods.is(Given) then mods | Synthetic else mods + val classMods = if mods.is(Given) then mods &~ Inline | Synthetic else mods cpy.TypeDef(cdef: TypeDef)( name = className, rhs = cpy.Template(impl)(constr, parents1, clsDerived, self1, diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 63b6e9f8ac51..3a34b57bd4fe 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -3626,7 +3626,7 @@ object Parsers { val templ = if isStatSep || isStatSeqEnd then Template(constr, parents, Nil, EmptyValDef, Nil) else withTemplate(constr, parents) - if noParams then ModuleDef(name, templ) + if noParams && !mods.is(Inline) then ModuleDef(name, templ) else TypeDef(name.toTypeName, templ) end gdef finalizeDef(gdef, mods1, start) diff --git a/tests/neg/i14177a.scala b/tests/neg/i14177a.scala new file mode 100644 index 000000000000..3031271c369b --- /dev/null +++ b/tests/neg/i14177a.scala @@ -0,0 +1,6 @@ +import scala.compiletime.* + +trait C[A] + +inline given [Tup <: Tuple]: C[Tup] with + val cs = summonAll[Tuple.Map[Tup, C]] // error cannot reduce inline match with diff --git a/tests/pos/i14177b.scala b/tests/pos/i14177b.scala new file mode 100644 index 000000000000..6da9a72ae551 --- /dev/null +++ b/tests/pos/i14177b.scala @@ -0,0 +1,15 @@ +class T + +inline given fail1: T with + val cs = scala.compiletime.summonAll[EmptyTuple] +inline given fail2[X]: T with + val cs = scala.compiletime.summonAll[EmptyTuple] +inline given fail3(using DummyImplicit): T with + val cs = scala.compiletime.summonAll[EmptyTuple] + +inline given ok1: T = new T: + val cs = scala.compiletime.summonAll[EmptyTuple] +inline given ok2[X]: T = new T: + val cs = scala.compiletime.summonAll[EmptyTuple] +inline given ok3(using DummyImplicit): T = new T: + val cs = scala.compiletime.summonAll[EmptyTuple] diff --git a/tests/pos/i14282.scala b/tests/pos/i14282.scala new file mode 100644 index 000000000000..2cc3ff1226e2 --- /dev/null +++ b/tests/pos/i14282.scala @@ -0,0 +1,13 @@ +trait Foo[A] { + inline def foo(): Unit +} + +inline given FooA[A]: Foo[A] with { + inline def foo(): Unit = println() +} +def test1 = FooA.foo() + +inline given FooInt: Foo[Int] with { + inline def foo(): Unit = println() +} +def test2 = FooInt.foo() From b0929f2b667b6d5a5c5eb5e6bd3f1e2c9c8dbc1c Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 18 Jan 2022 11:55:23 +0100 Subject: [PATCH 2/5] Disallow `transparent inline given .. with ..` --- compiler/src/dotty/tools/dotc/ast/Desugar.scala | 4 +++- tests/neg/i14177c.scala | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 tests/neg/i14177c.scala diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 75e300118562..bce9613a3290 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -809,7 +809,9 @@ object desugar { Nil } } - val classMods = if mods.is(Given) then mods &~ Inline | Synthetic else mods + if mods.isAllOf(Given | Inline | Transparent) then + report.error("inline non-alias given cannot be trasparent", cdef) + val classMods = if mods.is(Given) then mods &~ (Inline | Transparent) | Synthetic else mods cpy.TypeDef(cdef: TypeDef)( name = className, rhs = cpy.Template(impl)(constr, parents1, clsDerived, self1, diff --git a/tests/neg/i14177c.scala b/tests/neg/i14177c.scala new file mode 100644 index 000000000000..d281938ca0ea --- /dev/null +++ b/tests/neg/i14177c.scala @@ -0,0 +1,15 @@ +class T + +transparent inline given fail1: T with // error + val cs = scala.compiletime.summonAll[EmptyTuple] +transparent inline given fail2[X]: T with // error + val cs = scala.compiletime.summonAll[EmptyTuple] +transparent inline given fail3(using DummyImplicit): T with // error + val cs = scala.compiletime.summonAll[EmptyTuple] + +transparent inline given ok1: T = new T: + val cs = scala.compiletime.summonAll[EmptyTuple] +transparent inline given ok2[X]: T = new T: + val cs = scala.compiletime.summonAll[EmptyTuple] +transparent inline given ok3(using DummyImplicit): T = new T: + val cs = scala.compiletime.summonAll[EmptyTuple] From e26f86735059445c116f2886659f14ec0cf16263 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 18 Jan 2022 12:02:18 +0100 Subject: [PATCH 3/5] Add documentation on inline given instances --- compiler/src/dotty/tools/dotc/ast/Desugar.scala | 2 +- docs/docs/reference/contextual/givens.md | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index bce9613a3290..920871210eee 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -810,7 +810,7 @@ object desugar { } } if mods.isAllOf(Given | Inline | Transparent) then - report.error("inline non-alias given cannot be trasparent", cdef) + report.error("inline given instances cannot be trasparent", cdef) val classMods = if mods.is(Given) then mods &~ (Inline | Transparent) | Synthetic else mods cpy.TypeDef(cdef: TypeDef)( name = className, diff --git a/docs/docs/reference/contextual/givens.md b/docs/docs/reference/contextual/givens.md index 9b90040c01c3..e65e6153a579 100644 --- a/docs/docs/reference/contextual/givens.md +++ b/docs/docs/reference/contextual/givens.md @@ -100,6 +100,21 @@ transparent inline given mkAnnotations[A, T]: Annotations[A, T] = ${ Since `mkAnnotations` is `transparent`, the type of an application is the type of its right-hand side, which can be a proper subtype of the declared result type `Annotations[A, T]`. +Given instances can have the `inline` but not `transparent` modifiers as their type is already known from the signature. +Example: + +```scala +trait Show[T] { + inline def show(x: T): String +} + +inline given Show[Foo] with { + /*transparent*/ inline def show(x: Foo): String = ${ ... } +} +``` +Note that the inline methods within the given instances may be `transparent`. + + ## Pattern-Bound Given Instances Given instances can also appear in patterns. Example: From 5664de0b0e6981a97f946d02863462c138970670 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 18 Jan 2022 13:05:37 +0100 Subject: [PATCH 4/5] Expand inline given instance docs --- docs/docs/reference/contextual/givens.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/docs/reference/contextual/givens.md b/docs/docs/reference/contextual/givens.md index e65e6153a579..1f613ab6a495 100644 --- a/docs/docs/reference/contextual/givens.md +++ b/docs/docs/reference/contextual/givens.md @@ -114,6 +114,9 @@ inline given Show[Foo] with { ``` Note that the inline methods within the given instances may be `transparent`. +The inlining of given instances will not inline/duplicate the implementation of the given, it will just inline the instantiation of that instance. +This is used to help dead code elimination of the given instances that are not used after inlining. + ## Pattern-Bound Given Instances From 09a46f1c9b0c1186e80bae4e8f01927e6fca1dd4 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 26 Jan 2022 11:56:58 +0100 Subject: [PATCH 5/5] Add example --- docs/docs/reference/contextual/givens.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/docs/reference/contextual/givens.md b/docs/docs/reference/contextual/givens.md index 1f613ab6a495..2af94c0a8aea 100644 --- a/docs/docs/reference/contextual/givens.md +++ b/docs/docs/reference/contextual/givens.md @@ -111,6 +111,10 @@ trait Show[T] { inline given Show[Foo] with { /*transparent*/ inline def show(x: Foo): String = ${ ... } } + +def app = + // inlines `show` method call and removes the call to `given Show[Foo]` + summon[Show[Foo]].show(foo) ``` Note that the inline methods within the given instances may be `transparent`.