From ddfc83de1d26609ef135936943a14765f716fed1 Mon Sep 17 00:00:00 2001 From: Eugene Flesselle Date: Wed, 27 Mar 2024 15:20:21 +0100 Subject: [PATCH 1/3] Map over `ImportType`s in inliner tree type map The inliner replaces references to parameters by their corresponding proxys, including in singleton types. It did not, however, handle the mapping over import types, the symbols of which way have depended on parameters. Mapping imports correctly was necessary for i19493 since the `summonInline` resolves post inlining to a given imported within the inline definition. Fix #19493 [Cherry-picked 413d7b497b38656e480c13228e709653bd735d45] --- .../dotty/tools/dotc/inlines/Inliner.scala | 5 ++++ tests/pos/i19493.scala | 29 +++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 tests/pos/i19493.scala diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index 0e5f51daafdb..73d8b6370456 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -565,6 +565,11 @@ class Inliner(val call: tpd.Tree)(using Context): def apply(t: Type) = t match { case t: ThisType => thisProxy.getOrElse(t.cls, t) case t: TypeRef => paramProxy.getOrElse(t, mapOver(t)) + case t: TermRef if t.symbol.isImport => + val ImportType(e) = t.widenTermRefExpr: @unchecked + paramProxy.get(e.tpe) match + case Some(p) => newImportSymbol(ctx.owner, singleton(p)).termRef + case None => mapOver(t) case t: SingletonType => if t.termSymbol.isAllOf(InlineParam) then apply(t.widenTermRefExpr) else paramProxy.getOrElse(t, mapOver(t)) diff --git a/tests/pos/i19493.scala b/tests/pos/i19493.scala new file mode 100644 index 000000000000..37af3214ce16 --- /dev/null +++ b/tests/pos/i19493.scala @@ -0,0 +1,29 @@ + +import scala.compiletime.{summonAll, summonInline} +import deriving.Mirror + +type Sc[X] = X +case class Row[T[_]](name: T[String]) + +class DialectTypeMappers: + given String = ??? + +inline def metadata(dialect: DialectTypeMappers)(using m: Mirror.Of[Row[Sc]]): m.MirroredElemTypes = + import dialect.given + summonAll[m.MirroredElemTypes] + +def f = metadata(???) + + +object Minimization: + + class GivesString: + given aString: String = ??? + + inline def foo(x: GivesString): Unit = + import x.aString + summon[String] + summonInline[String] // was error + + foo(???) +end Minimization From 191b41ad811f70efe3a8bf25e1c26e4bde31f774 Mon Sep 17 00:00:00 2001 From: Eugene Flesselle Date: Thu, 27 Jun 2024 12:44:02 +0200 Subject: [PATCH 2/3] Also handle `ImportType`s depending on `this` references [Cherry-picked 969da606bc2b080e1709bc4d0407fe549dd3aa57] --- .../src/dotty/tools/dotc/inlines/Inliner.scala | 5 ++--- tests/pos/i19493.scala | 15 ++++++++++++++- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index 73d8b6370456..934b9d3a81b0 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -567,9 +567,8 @@ class Inliner(val call: tpd.Tree)(using Context): case t: TypeRef => paramProxy.getOrElse(t, mapOver(t)) case t: TermRef if t.symbol.isImport => val ImportType(e) = t.widenTermRefExpr: @unchecked - paramProxy.get(e.tpe) match - case Some(p) => newImportSymbol(ctx.owner, singleton(p)).termRef - case None => mapOver(t) + val e1 = singleton(apply(e.tpe)) + newImportSymbol(ctx.owner, e1).termRef case t: SingletonType => if t.termSymbol.isAllOf(InlineParam) then apply(t.widenTermRefExpr) else paramProxy.getOrElse(t, mapOver(t)) diff --git a/tests/pos/i19493.scala b/tests/pos/i19493.scala index 37af3214ce16..93d9023d589c 100644 --- a/tests/pos/i19493.scala +++ b/tests/pos/i19493.scala @@ -22,8 +22,21 @@ object Minimization: inline def foo(x: GivesString): Unit = import x.aString - summon[String] + summon[String] // ok summonInline[String] // was error foo(???) + + + trait A: + val x: GivesString + + inline def bar: Unit = + import this.x.aString + summon[String] // ok + summonInline[String] // was error + + val a: A = ??? + a.bar + end Minimization From 1b9b913c49a395ac2e7e76b0b48d7a8f0aadf640 Mon Sep 17 00:00:00 2001 From: Eugene Flesselle Date: Thu, 27 Jun 2024 19:32:36 +0200 Subject: [PATCH 3/3] Also handle imports on parameters of lambdas returned from inline defs Both i19493 and i19436 require mapping the type of the expr in an `ImportType` which is itself the info of a `TermRef`. In the first issue, for the substitution of an inline def parameter proxy. In the second issue, for the parameter of a lambda returned from an inline def. Both can be handled in `TypeMap` by mapping over references to `ImportType`s. The second case also requires modifying `TreeTypeMap#mapType` such that the logic mapping over imports is done within a `TypeMap` doing the symbol substitutions. Fixes #19436 [Cherry-picked ff003fdd3d898fd509f2b551d308eb723dd7c60c] --- .../src/dotty/tools/dotc/ast/TreeTypeMap.scala | 7 ++++++- compiler/src/dotty/tools/dotc/core/Types.scala | 6 ++++++ .../src/dotty/tools/dotc/inlines/Inliner.scala | 4 ---- tests/pos-macros/i19436/Macro_1.scala | 18 ++++++++++++++++++ tests/pos-macros/i19436/Test_2.scala | 2 ++ tests/pos/i19493.scala | 9 ++++++++- 6 files changed, 40 insertions(+), 6 deletions(-) create mode 100644 tests/pos-macros/i19436/Macro_1.scala create mode 100644 tests/pos-macros/i19436/Test_2.scala diff --git a/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala b/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala index 4d26781cd7be..5ddbdd990182 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala @@ -69,7 +69,12 @@ class TreeTypeMap( } def mapType(tp: Type): Type = - mapOwnerThis(typeMap(tp).substSym(substFrom, substTo)) + val substMap = new TypeMap(): + def apply(tp: Type): Type = tp match + case tp: TermRef if tp.symbol.isImport => mapOver(tp) + case tp => tp.substSym(substFrom, substTo) + mapOwnerThis(substMap(typeMap(tp))) + end mapType private def updateDecls(prevStats: List[Tree], newStats: List[Tree]): Unit = if (prevStats.isEmpty) assert(newStats.isEmpty) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 20176379023b..9d0b3d839619 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -5797,6 +5797,12 @@ object Types extends TypeUtils { val ctx = this.mapCtx // optimization for performance given Context = ctx tp match { + case tp: TermRef if tp.symbol.isImport => + // see tests/pos/i19493.scala for examples requiring mapping over imports + val ImportType(e) = tp.info: @unchecked + val e1 = singleton(apply(e.tpe)) + newImportSymbol(tp.symbol.owner, e1).termRef + case tp: NamedType => if stopBecauseStaticOrLocal(tp) then tp else diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index 934b9d3a81b0..0e5f51daafdb 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -565,10 +565,6 @@ class Inliner(val call: tpd.Tree)(using Context): def apply(t: Type) = t match { case t: ThisType => thisProxy.getOrElse(t.cls, t) case t: TypeRef => paramProxy.getOrElse(t, mapOver(t)) - case t: TermRef if t.symbol.isImport => - val ImportType(e) = t.widenTermRefExpr: @unchecked - val e1 = singleton(apply(e.tpe)) - newImportSymbol(ctx.owner, e1).termRef case t: SingletonType => if t.termSymbol.isAllOf(InlineParam) then apply(t.widenTermRefExpr) else paramProxy.getOrElse(t, mapOver(t)) diff --git a/tests/pos-macros/i19436/Macro_1.scala b/tests/pos-macros/i19436/Macro_1.scala new file mode 100644 index 000000000000..689f64203131 --- /dev/null +++ b/tests/pos-macros/i19436/Macro_1.scala @@ -0,0 +1,18 @@ + +import scala.quoted.* +import scala.compiletime.summonInline + +trait SomeImplicits: + given int: Int + +object Macro: + + transparent inline def testSummon: SomeImplicits => Int = ${ testSummonImpl } + + private def testSummonImpl(using Quotes): Expr[SomeImplicits => Int] = + import quotes.reflect.* + '{ + (x: SomeImplicits) => + import x.given + summonInline[Int] + } \ No newline at end of file diff --git a/tests/pos-macros/i19436/Test_2.scala b/tests/pos-macros/i19436/Test_2.scala new file mode 100644 index 000000000000..aedaf1cb87fb --- /dev/null +++ b/tests/pos-macros/i19436/Test_2.scala @@ -0,0 +1,2 @@ + +def fn: Unit = Macro.testSummon diff --git a/tests/pos/i19493.scala b/tests/pos/i19493.scala index 93d9023d589c..082f1450fd9e 100644 --- a/tests/pos/i19493.scala +++ b/tests/pos/i19493.scala @@ -1,4 +1,3 @@ - import scala.compiletime.{summonAll, summonInline} import deriving.Mirror @@ -39,4 +38,12 @@ object Minimization: val a: A = ??? a.bar + + inline def baz() = (x: GivesString) => + import x.aString + summon[String] // ok + summonInline[String] // was error + + baz() + end Minimization