From 581ad05a65a57bf4e77b63b60c17cc72eb180cbe 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 --- .../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 7c79e972c126..6116c00aeff7 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 54819b0aaa29e14d2b1198d78463cc9aeaeb6527 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 --- .../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 6116c00aeff7..a83c5eaa3ba8 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 a16ed3814936440eaf2d3d3a1162f7d4b09cf92e 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 --- .../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 668daea5f1fd..98d9a0ca85f6 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 6d84242648b2..aa1813f572f7 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -6295,6 +6295,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 a83c5eaa3ba8..7c79e972c126 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