From 84ccfc7a6509ef919d2cbd5d65543a98ea59fc92 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 4 Apr 2019 14:52:49 +0200 Subject: [PATCH 1/4] Fix Scope.exists --- compiler/src/dotty/tools/dotc/core/Scopes.scala | 2 +- compiler/src/dotty/tools/dotc/transform/YCheckPositions.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Scopes.scala b/compiler/src/dotty/tools/dotc/core/Scopes.scala index c9af4067d37a..1b6d600612a1 100644 --- a/compiler/src/dotty/tools/dotc/core/Scopes.scala +++ b/compiler/src/dotty/tools/dotc/core/Scopes.scala @@ -106,7 +106,7 @@ object Scopes { } /** Tests whether a predicate holds for at least one Symbol of this Scope. */ - def exists(p: Symbol => Boolean)(implicit ctx: Context): Boolean = filter(p).isEmpty + def exists(p: Symbol => Boolean)(implicit ctx: Context): Boolean = filter(p).nonEmpty /** Finds the first Symbol of this Scope satisfying a predicate, if any. */ def find(p: Symbol => Boolean)(implicit ctx: Context): Symbol = filter(p) match { diff --git a/compiler/src/dotty/tools/dotc/transform/YCheckPositions.scala b/compiler/src/dotty/tools/dotc/transform/YCheckPositions.scala index 12ac4b6240c0..74108fdb013f 100644 --- a/compiler/src/dotty/tools/dotc/transform/YCheckPositions.scala +++ b/compiler/src/dotty/tools/dotc/transform/YCheckPositions.scala @@ -58,7 +58,7 @@ class YCheckPositions extends Phases.Phase { private def isMacro(call: Tree)(implicit ctx: Context) = { if (ctx.phase <= ctx.typerPhase.next) call.symbol.is(Macro) - else (call.symbol.unforcedDecls.exists(_.is(Macro)) || call.symbol.unforcedDecls.toList.exists(_.is(Macro))) + else (call.symbol.unforcedDecls.isEmpty /* bug from exists */ || call.symbol.unforcedDecls.exists(_.is(Macro)) || call.symbol.unforcedDecls.toList.exists(_.is(Macro))) } } From 83047c4b2795218bda6df906ef8a2f106cb4279a Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 4 Apr 2019 17:44:50 +0200 Subject: [PATCH 2/4] Add missing Inlined node for beta-reduced by-name function --- compiler/src/dotty/tools/dotc/typer/Inliner.scala | 6 +++--- tests/run/i4431-b/quoted_1.scala | 5 +++++ tests/run/i4431-b/quoted_2.scala | 8 ++++++++ 3 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 tests/run/i4431-b/quoted_1.scala create mode 100644 tests/run/i4431-b/quoted_2.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 772668f897e1..76a3b550729d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -250,7 +250,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { * @param bindingsBuf the buffer to which the definition should be appended */ private def paramBindingDef(name: Name, paramtp: Type, arg: Tree, - bindingsBuf: mutable.ListBuffer[ValOrDefDef]): ValOrDefDef = { + bindingsBuf: mutable.ListBuffer[ValOrDefDef])(implicit ctx: Context): ValOrDefDef = { val argtpe = arg.tpe.dealiasKeepAnnots val isByName = paramtp.dealias.isInstanceOf[ExprType] var inlineFlag = InlineProxy @@ -694,7 +694,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { val argSyms = (mt.paramNames, mt.paramInfos, args).zipped.map { (name, paramtp, arg) => arg.tpe.dealias match { case ref @ TermRef(NoPrefix, _) => ref.symbol - case _ => paramBindingDef(name, paramtp, arg, bindingsBuf).symbol + case _ => paramBindingDef(name, paramtp, arg, bindingsBuf)(ctx.withSource(cl.source)).symbol } } val expander = new TreeTypeMap( @@ -702,7 +702,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { newOwners = ctx.owner :: Nil, substFrom = ddef.vparamss.head.map(_.symbol), substTo = argSyms) - seq(bindingsBuf.toList, expander.transform(ddef.rhs)) + Inlined(ddef, bindingsBuf.toList, expander.transform(ddef.rhs)) case _ => tree } case _ => tree diff --git a/tests/run/i4431-b/quoted_1.scala b/tests/run/i4431-b/quoted_1.scala new file mode 100644 index 000000000000..e62e45ddb2c2 --- /dev/null +++ b/tests/run/i4431-b/quoted_1.scala @@ -0,0 +1,5 @@ +import scala.quoted._ + +object Macros { + inline def h(f: => Int => String): String = f(42) +} diff --git a/tests/run/i4431-b/quoted_2.scala b/tests/run/i4431-b/quoted_2.scala new file mode 100644 index 000000000000..00cdb38258d1 --- /dev/null +++ b/tests/run/i4431-b/quoted_2.scala @@ -0,0 +1,8 @@ +import scala.quoted._ +import Macros._ + +object Test { + def main(args: Array[String]): Unit = { + println(h(x => "abc" + x)) + } +} From c1e031f01f244490d86d6f0736fc576287dec59e Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 4 Apr 2019 17:46:22 +0200 Subject: [PATCH 3/4] Add missing context source change --- compiler/src/dotty/tools/dotc/transform/YCheckPositions.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/YCheckPositions.scala b/compiler/src/dotty/tools/dotc/transform/YCheckPositions.scala index 74108fdb013f..c3726e4f246b 100644 --- a/compiler/src/dotty/tools/dotc/transform/YCheckPositions.scala +++ b/compiler/src/dotty/tools/dotc/transform/YCheckPositions.scala @@ -40,13 +40,13 @@ class YCheckPositions extends Phases.Phase { assert(bindings.isEmpty) val old = sources sources = old.tail - traverse(expansion)(inlineContext(EmptyTree)) + traverse(expansion)(inlineContext(EmptyTree).withSource(sources.head)) sources = old case Inlined(call, bindings, expansion) => bindings.foreach(traverse(_)) sources = call.symbol.topLevelClass.source :: sources if (!isMacro(call)) // FIXME macro implementations can drop Inlined nodes. We should reinsert them after macro expansion based on the positions of the trees - traverse(expansion)(inlineContext(call)) + traverse(expansion)(inlineContext(call).withSource(sources.head)) sources = sources.tail case _ => traverseChildren(tree) } From e219f7eb00da5d89c954559ee2ec7aa46e32bae4 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 4 Apr 2019 17:53:54 +0200 Subject: [PATCH 4/4] Add workaround to identify after PostTyper Inlined that came macros --- compiler/src/dotty/tools/dotc/transform/PostTyper.scala | 3 ++- .../src/dotty/tools/dotc/transform/YCheckPositions.scala | 5 +++-- compiler/src/dotty/tools/dotc/typer/Inliner.scala | 6 ++++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 3749f73b6bbe..e69f8fc727f1 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -224,7 +224,8 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase super.transform(tree1) } case Inlined(call, bindings, expansion) if !call.isEmpty => - val callTrace = Inliner.inlineCallTrace(call.symbol, call.sourcePos) + val pos = call.sourcePos + val callTrace = Inliner.inlineCallTrace(call.symbol, pos)(ctx.withSource(pos.source)) cpy.Inlined(tree)(callTrace, transformSub(bindings), transform(expansion)(inlineContext(call))) case tree: Template => withNoCheckNews(tree.parents.flatMap(newPart)) { diff --git a/compiler/src/dotty/tools/dotc/transform/YCheckPositions.scala b/compiler/src/dotty/tools/dotc/transform/YCheckPositions.scala index c3726e4f246b..33cdb5f991d3 100644 --- a/compiler/src/dotty/tools/dotc/transform/YCheckPositions.scala +++ b/compiler/src/dotty/tools/dotc/transform/YCheckPositions.scala @@ -57,8 +57,9 @@ class YCheckPositions extends Phases.Phase { } private def isMacro(call: Tree)(implicit ctx: Context) = { - if (ctx.phase <= ctx.typerPhase.next) call.symbol.is(Macro) - else (call.symbol.unforcedDecls.isEmpty /* bug from exists */ || call.symbol.unforcedDecls.exists(_.is(Macro)) || call.symbol.unforcedDecls.toList.exists(_.is(Macro))) + if (ctx.phase <= ctx.postTyperPhase) call.symbol.is(Macro) + else call.isInstanceOf[Select] // The call of a macro after typer is encoded as a Select while other inlines are Ident + // TODO remove this distinction once Inline nodes of expanded macros can be trusted (also in Inliner.inlineCallTrace) } } diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 76a3b550729d..8a39aae74de0 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -180,10 +180,12 @@ object Inliner { * - the call's position * in the call field of an Inlined node. * The trace has enough info to completely reconstruct positions. + * Note: For macros it returns a Select and for other inline methods it returns an Ident (this distinction is only temporary to be able to run YCheckPositions) */ def inlineCallTrace(callSym: Symbol, pos: SourcePosition)(implicit ctx: Context): Tree = { - implicit val src = pos.source - Ident(callSym.topLevelClass.typeRef).withSpan(pos.span) + assert(ctx.source == pos.source) + if (callSym.is(Macro)) ref(callSym.topLevelClass.owner).select(callSym.topLevelClass.name).withSpan(pos.span) + else Ident(callSym.topLevelClass.typeRef).withSpan(pos.span) } }