From dde8fdc4838329b44355f425baa301da79b17ca4 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 22 Oct 2020 13:22:20 +0200 Subject: [PATCH 01/56] Inline non-transparent calls after Pickler --- compiler/src/dotty/tools/dotc/Compiler.scala | 1 + compiler/src/dotty/tools/dotc/Run.scala | 20 +++++++++-- .../tools/dotc/config/ScalaSettings.scala | 2 ++ .../src/dotty/tools/dotc/core/Phases.scala | 4 +++ .../dotty/tools/dotc/transform/Inlining.scala | 24 +++++++++++-- .../dotc/transform/TupleOptimizations.scala | 2 +- .../dotc/transform/YCheckPositions.scala | 10 +++--- .../src/dotty/tools/dotc/typer/Checking.scala | 8 +++-- .../src/dotty/tools/dotc/typer/FrontEnd.scala | 16 +-------- .../src/dotty/tools/dotc/typer/Inliner.scala | 22 +++++++++--- .../src/dotty/tools/dotc/typer/ReTyper.scala | 2 +- .../src/dotty/tools/dotc/typer/Typer.scala | 10 ++++-- .../test/dotc/run-test-pickling.blacklist | 34 +++++++++++-------- library/src/scala/Tuple.scala | 28 +++++++-------- library/src/scala/compiletime/package.scala | 6 ++-- .../scala/compiletime/testing/package.scala | 4 +-- library/src/scala/quoted/ToExpr.scala | 5 +-- .../scala/runtime/stdLibPatches/Predef.scala | 12 +++---- tests/neg-macros/i7618.scala | 2 +- tests/neg-macros/i9014/Macros_1.scala | 2 +- tests/neg-macros/i9014b.check | 6 ++++ tests/neg-macros/i9014b/Macros_1.scala | 4 +++ tests/neg-macros/i9014b/Test_2.scala | 1 + tests/neg/cannot-reduce-summonFrom.scala | 2 +- tests/neg/i11101.check | 8 +++++ tests/neg/i11101.scala | 17 ++++++++++ tests/neg/i8841.check | 5 ++- tests/neg/i9014.check | 9 ++--- tests/neg/i9014.scala | 2 +- tests/neg/inlinevals-2.scala | 12 +++++++ tests/neg/inlinevals.scala | 2 +- tests/neg/power2.scala | 14 ++++++++ tests/pos-macros/i6588.scala | 2 +- tests/pos-macros/i7558/Macro_1.scala | 8 +++++ tests/pos-macros/i7558/Test_2.scala | 1 + tests/pos-macros/i8858/Macro_1.scala | 9 +++++ tests/pos-macros/i8858/Test_2.scala | 3 ++ tests/pos-macros/i9296/Test_2.scala | 3 +- tests/pos/i10107c.scala | 12 +++++++ tests/pos/i10295.scala | 2 +- tests/pos/i11184a.scala | 5 +++ tests/pos/i11184b.scala | 5 +++ tests/pos/i11184c.scala | 6 ++++ tests/pos/i8892.scala | 2 +- tests/pos/inline-separate/A_1.scala | 2 +- tests/pos/inline-separate/Test_2.scala | 2 +- tests/pos/summonInline.scala | 10 ++++++ .../Yretain-trees/tasty-definitions-3.check | 4 +-- .../Yretain-trees/tasty-load-tree-2.check | 4 +-- .../beta-reduce-inline-result.check | 4 +-- tests/run-macros/i10043/Macro_1.scala | 13 +++++++ tests/run-macros/i10043/Test_2.scala | 1 + tests/run-macros/i7025b/Macros_1.scala | 21 ++++++++++++ tests/run-macros/i7025b/Test_2.scala | 9 +++++ tests/run-macros/i8877/Macros_1.scala | 20 +++++++++++ tests/run-macros/i8877/Test_2.scala | 5 +++ tests/run-macros/no-symbol/2.scala | 2 +- .../run-macros/reflect-inline/assert_1.scala | 4 +-- tests/run-macros/reflect-sourceCode.check | 7 ++++ .../reflect-sourceCode/Test_2.scala | 14 ++++---- .../reflect-typeChecks/assert_1.scala | 4 +-- .../string-context-implicits/Macro_1.scala | 5 +-- tests/run-macros/tasty-extractors-2.check | 2 +- .../whitebox-inline-macro/Macro_1.scala | 11 ++++++ .../whitebox-inline-macro/Test_2.scala | 12 +++++++ tests/run-staging/i11184.scala | 22 ++++++++++++ tests/run/i9011.scala | 4 +-- tests/run/typeclass-derivation2b.scala | 8 ++--- tests/run/typeclass-derivation3.scala | 34 +++++++++---------- tests/run/whitebox-inline.scala | 16 +++++++++ 70 files changed, 453 insertions(+), 141 deletions(-) create mode 100644 tests/neg-macros/i9014b.check create mode 100644 tests/neg-macros/i9014b/Macros_1.scala create mode 100644 tests/neg-macros/i9014b/Test_2.scala create mode 100644 tests/neg/i11101.check create mode 100644 tests/neg/i11101.scala create mode 100644 tests/neg/inlinevals-2.scala create mode 100644 tests/neg/power2.scala create mode 100644 tests/pos-macros/i7558/Macro_1.scala create mode 100644 tests/pos-macros/i7558/Test_2.scala create mode 100644 tests/pos-macros/i8858/Macro_1.scala create mode 100644 tests/pos-macros/i8858/Test_2.scala create mode 100644 tests/pos/i10107c.scala create mode 100644 tests/pos/i11184a.scala create mode 100644 tests/pos/i11184b.scala create mode 100644 tests/pos/i11184c.scala create mode 100644 tests/pos/summonInline.scala create mode 100644 tests/run-macros/i10043/Macro_1.scala create mode 100644 tests/run-macros/i10043/Test_2.scala create mode 100644 tests/run-macros/i7025b/Macros_1.scala create mode 100644 tests/run-macros/i7025b/Test_2.scala create mode 100644 tests/run-macros/i8877/Macros_1.scala create mode 100644 tests/run-macros/i8877/Test_2.scala create mode 100644 tests/run-macros/reflect-sourceCode.check create mode 100644 tests/run-macros/whitebox-inline-macro/Macro_1.scala create mode 100644 tests/run-macros/whitebox-inline-macro/Test_2.scala create mode 100644 tests/run-staging/i11184.scala create mode 100644 tests/run/whitebox-inline.scala diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index f24df22d673d..f20db4d9560b 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -50,6 +50,7 @@ class Compiler { /** Phases dealing with TASTY tree pickling and unpickling */ protected def picklerPhases: List[List[Phase]] = List(new Pickler) :: // Generate TASTY info + List(new Inlining) :: // Inline and execute macros List(new PickleQuotes) :: // Turn quoted trees into explicit run-time data structures Nil diff --git a/compiler/src/dotty/tools/dotc/Run.scala b/compiler/src/dotty/tools/dotc/Run.scala index 4ee8475b41d0..6b51908c37d7 100644 --- a/compiler/src/dotty/tools/dotc/Run.scala +++ b/compiler/src/dotty/tools/dotc/Run.scala @@ -101,11 +101,27 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint */ def units: List[CompilationUnit] = myUnits - var suspendedUnits: mutable.ListBuffer[CompilationUnit] = mutable.ListBuffer() - private def units_=(us: List[CompilationUnit]): Unit = myUnits = us + var suspendedUnits: mutable.ListBuffer[CompilationUnit] = mutable.ListBuffer() + + def checkSuspendedUnits(newUnits: List[CompilationUnit])(using Context): Unit = + if newUnits.isEmpty && suspendedUnits.nonEmpty && !ctx.reporter.errorsReported then + val where = + if suspendedUnits.size == 1 then i"in ${suspendedUnits.head}." + else i"""among + | + | ${suspendedUnits.toList}%, % + |""" + val enableXprintSuspensionHint = + if ctx.settings.XprintSuspension.value then "" + else "\n\nCompiling with -Xprint-suspension gives more information." + report.error(em"""Cyclic macro dependencies $where + |Compilation stopped since no further progress can be made. + | + |To fix this, place macros in one set of files and their callers in another.$enableXprintSuspensionHint""") + /** The files currently being compiled (active or suspended). * This may return different results over time. * These files do not have to be source files since it's possible to compile diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index 3c039eb2212a..e08e96d9915a 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -227,6 +227,8 @@ class ScalaSettings extends Settings.SettingGroup with CommonScalaSettings { val Yinstrument: Setting[Boolean] = BooleanSetting("-Yinstrument", "Add instrumentation code that counts allocations and closure creations.") val YinstrumentDefs: Setting[Boolean] = BooleanSetting("-Yinstrument-defs", "Add instrumentation code that counts method calls; needs -Yinstrument to be set, too.") + val YinlineBlackboxWhileTyping: Setting[Boolean] = BooleanSetting("-Yinline-blackbox-while-typing", "") + /** Dottydoc specific settings that are not used in scaladoc */ val docSnapshot: Setting[Boolean] = BooleanSetting("-doc-snapshot", "Generate a documentation snapshot for the current Dotty version") diff --git a/compiler/src/dotty/tools/dotc/core/Phases.scala b/compiler/src/dotty/tools/dotc/core/Phases.scala index 5c0725e17c4b..4646751192b4 100644 --- a/compiler/src/dotty/tools/dotc/core/Phases.scala +++ b/compiler/src/dotty/tools/dotc/core/Phases.scala @@ -198,6 +198,7 @@ object Phases { private var myPostTyperPhase: Phase = _ private var mySbtExtractDependenciesPhase: Phase = _ private var myPicklerPhase: Phase = _ + private var myInliningPhase: Phase = _ private var myPickleQuotesPhase: Phase = _ private var myFirstTransformPhase: Phase = _ private var myCollectNullableFieldsPhase: Phase = _ @@ -218,6 +219,7 @@ object Phases { final def postTyperPhase: Phase = myPostTyperPhase final def sbtExtractDependenciesPhase: Phase = mySbtExtractDependenciesPhase final def picklerPhase: Phase = myPicklerPhase + final def inliningPhase: Phase = myInliningPhase final def pickleQuotesPhase: Phase = myPickleQuotesPhase final def firstTransformPhase: Phase = myFirstTransformPhase final def collectNullableFieldsPhase: Phase = myCollectNullableFieldsPhase @@ -241,6 +243,7 @@ object Phases { myPostTyperPhase = phaseOfClass(classOf[PostTyper]) mySbtExtractDependenciesPhase = phaseOfClass(classOf[sbt.ExtractDependencies]) myPicklerPhase = phaseOfClass(classOf[Pickler]) + myInliningPhase = phaseOfClass(classOf[Inlining]) myPickleQuotesPhase = phaseOfClass(classOf[PickleQuotes]) myFirstTransformPhase = phaseOfClass(classOf[FirstTransform]) myCollectNullableFieldsPhase = phaseOfClass(classOf[CollectNullableFields]) @@ -406,6 +409,7 @@ object Phases { def postTyperPhase(using Context): Phase = ctx.base.postTyperPhase def sbtExtractDependenciesPhase(using Context): Phase = ctx.base.sbtExtractDependenciesPhase def picklerPhase(using Context): Phase = ctx.base.picklerPhase + def inliningPhase(using Context): Phase = ctx.base.inliningPhase def pickleQuotesPhase(using Context): Phase = ctx.base.pickleQuotesPhase def firstTransformPhase(using Context): Phase = ctx.base.firstTransformPhase def refchecksPhase(using Context): Phase = ctx.base.refchecksPhase diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala index 70895a647e39..113c0be0f5a9 100644 --- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala @@ -38,6 +38,16 @@ class Inlining extends MacroTransform { override def allowsImplicitSearch: Boolean = true + override def run(using Context): Unit = + // if (!ctx.settings.YinlineBlackboxWhileTyping.value) // phase not needed? + try super.run + catch case _: CompilationUnit.SuspendException => () + + override def runOn(units: List[CompilationUnit])(using Context): List[CompilationUnit] = + val newUnits = super.runOn(units).filterNot(_.suspended) + ctx.run.checkSuspendedUnits(newUnits) + newUnits + override def checkPostCondition(tree: Tree)(using Context): Unit = tree match { case PackageDef(pid, _) if tree.symbol.owner == defn.RootClass => @@ -71,9 +81,11 @@ class Inlining extends MacroTransform { super.transform(tree) case _ if Inliner.isInlineable(tree) && !tree.tpe.widen.isInstanceOf[MethodOrPoly] && StagingContext.level == 0 => val tree1 = super.transform(tree) - val inlined = Inliner.inlineCall(tree1) - if tree1 eq inlined then inlined - else transform(inlined) + if tree1.tpe.isError then tree1 + else + val inlined = Inliner.inlineCall(tree1) + if tree1 eq inlined then inlined + else transform(inlined) // TODO can this be removed if `needsStaging` is set in `Inliner`? case _: GenericApply if tree.symbol.isQuote => ctx.compilationUnit.needsStaging = true super.transform(tree)(using StagingContext.quoteContext) @@ -88,3 +100,9 @@ class Inlining extends MacroTransform { object Inlining { val name: String = "inlining" } + +////// FIXME + +////// Issue: dotty.tools.dotc.transform.YCheckPositions$$anon$1.traverse(YCheckPositions.scala:33) +// tests/run/summonAll.scala +// tests/run/lst diff --git a/compiler/src/dotty/tools/dotc/transform/TupleOptimizations.scala b/compiler/src/dotty/tools/dotc/transform/TupleOptimizations.scala index d208c19c5c52..73e75597c2b3 100644 --- a/compiler/src/dotty/tools/dotc/transform/TupleOptimizations.scala +++ b/compiler/src/dotty/tools/dotc/transform/TupleOptimizations.scala @@ -173,7 +173,7 @@ class TupleOptimizations extends MiniPhase with IdentityDenotTransformer { val size = tpes.size if (size == 0) // Array.emptyObjectArray - ref(defn.ArrayModule).select("emptyObjectArray".toTermName).ensureApplied + ref(defn.ArrayModule).select("emptyObjectArray".toTermName).ensureApplied.withSpan(tree.span) else if (size <= MaxTupleArity) // scala.runtime.Tuples.productToArray(tup.asInstanceOf[Product]) ref(defn.RuntimeTuples_productToArray).appliedTo(tup.asInstance(defn.ProductClass.typeRef)) diff --git a/compiler/src/dotty/tools/dotc/transform/YCheckPositions.scala b/compiler/src/dotty/tools/dotc/transform/YCheckPositions.scala index 3f5da6bab7e7..233f20df9f4e 100644 --- a/compiler/src/dotty/tools/dotc/transform/YCheckPositions.scala +++ b/compiler/src/dotty/tools/dotc/transform/YCheckPositions.scala @@ -27,7 +27,7 @@ class YCheckPositions extends Phase { // Check current context is correct assert(ctx.source == sources.head) - if (!tree.isEmpty && !tree.isInstanceOf[untpd.TypedSplice] && ctx.typerState.isGlobalCommittable) + if (!tree.isEmpty && !tree.isInstanceOf[untpd.TypedSplice] && !tree.isInstanceOf[Inlined] && ctx.typerState.isGlobalCommittable) if (!tree.isType) { // TODO also check types, currently we do not add Inlined(EmptyTree, _, _) for types. We should. val currentSource = sources.head assert(tree.source == currentSource, i"wrong source set for $tree # ${tree.uniqueId} of ${tree.getClass}, set to ${tree.source} but context had $currentSource") @@ -55,8 +55,10 @@ class YCheckPositions extends Phase { } private def isMacro(call: Tree)(using Context) = - if (ctx.phase <= 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) + call.symbol.is(Macro) || + // 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) + (!(ctx.phase <= postTyperPhase) && call.isInstanceOf[Select]) + } diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 32bcbc0aab4b..c1f28c7f3681 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -901,11 +901,15 @@ trait Checking { /** Check that `tree` can be right hand-side or argument to `inline` value or parameter. */ def checkInlineConformant(tpt: Tree, tree: Tree, sym: Symbol)(using Context): Unit = { - if sym.is(Inline, butNot = DeferredOrTermParamOrAccessor) && !ctx.erasedTypes && !Inliner.inInlineMethod then + if sym.is(Inline, butNot = DeferredOrTermParamOrAccessor) + && !ctx.erasedTypes + && !Inliner.inInlineMethod + // && !(ctx.phase <= Phases.inliningPhase) + then tpt.tpe.widenTermRefExpr.dealias.normalized match case tp: ConstantType => if !(exprPurity(tree) >= Pure) then - report.error(em"inline value must be pure", tree.srcPos) + report.error(em"inline value must be pure: $tree", tree.srcPos) case _ => val pos = if tpt.span.isZeroExtent then tree.srcPos else tpt.srcPos report.error(em"inline value must have a literal constant type", pos) diff --git a/compiler/src/dotty/tools/dotc/typer/FrontEnd.scala b/compiler/src/dotty/tools/dotc/typer/FrontEnd.scala index 87307115bcda..0f51b19ddf91 100644 --- a/compiler/src/dotty/tools/dotc/typer/FrontEnd.scala +++ b/compiler/src/dotty/tools/dotc/typer/FrontEnd.scala @@ -122,21 +122,7 @@ class FrontEnd extends Phase { unitContexts.foreach(javaCheck(using _)) // after typechecking to avoid cycles val newUnits = unitContexts.map(_.compilationUnit).filterNot(discardAfterTyper) - val suspendedUnits = ctx.run.suspendedUnits - if newUnits.isEmpty && suspendedUnits.nonEmpty && !ctx.reporter.errorsReported then - val where = - if suspendedUnits.size == 1 then i"in ${suspendedUnits.head}." - else i"""among - | - | ${suspendedUnits.toList}%, % - |""" - val enableXprintSuspensionHint = - if ctx.settings.XprintSuspension.value then "" - else "\n\nCompiling with -Xprint-suspension gives more information." - report.error(em"""Cyclic macro dependencies $where - |Compilation stopped since no further progress can be made. - | - |To fix this, place macros in one set of files and their callers in another.$enableXprintSuspensionHint""") + ctx.run.checkSuspendedUnits(newUnits) newUnits def run(using Context): Unit = unsupported("run") diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 79e1b739b542..45af241ef36d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -43,7 +43,7 @@ object Inliner { * @pre hasBodyToInline(sym) */ def bodyToInline(sym: SymDenotation)(using Context): Tree = - if (sym.isInlineMethod && sym.hasAnnotation(defn.BodyAnnot)) + if hasBodyToInline(sym) then sym.getAnnotation(defn.BodyAnnot).get.tree else EmptyTree @@ -941,7 +941,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) { } } - def tryInline(tree: Tree)(using Context): Tree = tree match { + def tryInlineArg(tree: Tree)(using Context): Tree = tree match { case InlineableArg(rhs) => inlining.println(i"inline arg $tree -> $rhs") rhs @@ -1236,7 +1236,8 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) { } override def typedIdent(tree: untpd.Ident, pt: Type)(using Context): Tree = - tryInline(tree.asInstanceOf[tpd.Tree]) `orElse` super.typedIdent(tree, pt) + val tree1 = tryInlineArg(tree.asInstanceOf[tpd.Tree]) `orElse` super.typedIdent(tree, pt) + inlineIfIsNestedInlineCall(tree1) override def typedSelect(tree: untpd.Select, pt: Type)(using Context): Tree = { assert(tree.hasType, tree) @@ -1248,7 +1249,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) { else val res = resMaybeReduced ensureAccessible(res.tpe, tree.qualifier.isInstanceOf[untpd.Super], tree.srcPos) - res + inlineIfIsNestedInlineCall(res) } override def typedIf(tree: untpd.If, pt: Type)(using Context): Tree = @@ -1284,7 +1285,8 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) { && !suppressInline => val expanded = expandMacro(res.args.head, tree.span) typedExpr(expanded) // Inline calls and constant fold code generated by the macro - case res => res + case res => + inlineIfIsNestedInlineCall(res) } override def typedMatchFinish(tree: untpd.Match, sel: Tree, wideSelType: Type, cases: List[untpd.CaseDef], pt: Type)(using Context) = @@ -1335,6 +1337,16 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) { /** Suppress further inlining if this inline typer has already issued errors */ override def suppressInline(using Context) = ctx.reporter.errorCount > initialErrorCount || super.suppressInline + + private def inlineIfIsNestedInlineCall(tree: Tree)(using Context): Tree = + if + !suppressInline && + !tree.tpe.widenTermRefExpr.isInstanceOf[MethodOrPoly] && + Inliner.isInlineable(tree) && + StagingContext.level == 0 + then + Inliner.inlineCall(tree) + else tree } /** Drop any side-effect-free bindings that are unused in expansion or other reachable bindings. diff --git a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala index 0d34ffe1320e..0e91ce964620 100644 --- a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala +++ b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala @@ -118,7 +118,7 @@ class ReTyper extends Typer with ReChecking { try super.typedUnadapted(tree, pt, locked) catch { case NonFatal(ex) => - if (ctx.isAfterTyper) + if ctx.isAfterTyper && ctx.phase != Phases.inliningPhase then println(i"exception while typing $tree of class ${tree.getClass} # ${tree.uniqueId}") throw ex } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 7bd34bf8a414..36f592ca5913 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -3407,13 +3407,17 @@ class Typer extends Namer val meth = methPart(tree).symbol if meth.isAllOf(DeferredInline) && !Inliner.inInlineMethod then errorTree(tree, i"Deferred inline ${meth.showLocated} cannot be invoked") - else if (Inliner.isInlineable(tree) && !suppressInline && StagingContext.level == 0) { + else if + Inliner.isInlineable(tree) && + !suppressInline && + StagingContext.level == 0 && + (tree.symbol.is(Transparent) || ctx.settings.YinlineBlackboxWhileTyping.value) + then tree.tpe <:< wildApprox(pt) val errorCount = ctx.reporter.errorCount val inlined = Inliner.inlineCall(tree) if ((inlined ne tree) && errorCount == ctx.reporter.errorCount) readaptSimplified(inlined) else inlined - } else if (tree.symbol.isScala2Macro && // `raw`, `f` and `s` are eliminated by the StringInterpolatorOpt phase tree.symbol != defn.StringContext_raw && @@ -3732,7 +3736,7 @@ class Typer extends Namer } // Overridden in InlineTyper - def suppressInline(using Context): Boolean = ctx.isAfterTyper + def suppressInline(using Context): Boolean = ctx.isAfterTyper && ctx.phase.phaseName != "inlining" /** Does the "contextuality" of the method type `methType` match the one of the prototype `pt`? * This is the case if diff --git a/compiler/test/dotc/run-test-pickling.blacklist b/compiler/test/dotc/run-test-pickling.blacklist index aefe3b14b017..c997114e3015 100644 --- a/compiler/test/dotc/run-test-pickling.blacklist +++ b/compiler/test/dotc/run-test-pickling.blacklist @@ -1,30 +1,34 @@ +derive-generic.scala eff-dependent.scala +enum-java +i5257.scala +i7212 +i7868.scala +i9011.scala +i9473.scala +macros-in-same-project1 +mixin-forwarder-overload +t10889 t3452d t3452e t3452g t7374 -tuples1.scala -tuples1a.scala -tuples1b.scala +t8905 +tuple-drop.scala +tuple-ops.scala tuple-ops.scala tuple-take.scala -tuple-drop.scala tuple-zip.scala +tuples1.scala +tuples1a.scala +tuples1b.scala +typeclass-derivation-doc-example.scala typeclass-derivation1.scala typeclass-derivation2.scala typeclass-derivation2a.scala +typeclass-derivation2b.scala typeclass-derivation2c.scala typeclass-derivation2d.scala typeclass-derivation3.scala -derive-generic.scala -mixin-forwarder-overload -t8905 -t10889 -macros-in-same-project1 -i5257.scala -i7868.scala -enum-java -zero-arity-case-class.scala -tuple-ops.scala -i7212 varargs-abstract +zero-arity-case-class.scala diff --git a/library/src/scala/Tuple.scala b/library/src/scala/Tuple.scala index 87d6f26e0e6f..e927614bde98 100644 --- a/library/src/scala/Tuple.scala +++ b/library/src/scala/Tuple.scala @@ -7,32 +7,32 @@ sealed trait Tuple extends Product { import Tuple._ /** Create a copy this tuple as an Array */ - inline def toArray: Array[Object] = + transparent inline def toArray: Array[Object] = runtime.Tuples.toArray(this) /** Create a copy this tuple as a List */ - inline def toList: List[Union[this.type]] = + transparent inline def toList: List[Union[this.type]] = this.productIterator.toList .asInstanceOf[List[Union[this.type]]] /** Create a copy this tuple as an IArray */ - inline def toIArray: IArray[Object] = + transparent inline def toIArray: IArray[Object] = runtime.Tuples.toIArray(this) /** Return a new tuple by prepending the element to `this` tuple. * This operation is O(this.size) */ - inline def *: [H, This >: this.type <: Tuple] (x: H): H *: This = + transparent inline def *: [H, This >: this.type <: Tuple] (x: H): H *: This = runtime.Tuples.cons(x, this).asInstanceOf[H *: This] /** Return a new tuple by concatenating `this` tuple with `that` tuple. * This operation is O(this.size + that.size) */ - inline def ++ [This >: this.type <: Tuple](that: Tuple): Concat[This, that.type] = + transparent inline def ++ [This >: this.type <: Tuple](that: Tuple): Concat[This, that.type] = runtime.Tuples.concat(this, that).asInstanceOf[Concat[This, that.type]] /** Return the size (or arity) of the tuple */ - inline def size[This >: this.type <: Tuple]: Size[This] = + transparent inline def size[This >: this.type <: Tuple]: Size[This] = runtime.Tuples.size(this).asInstanceOf[Size[This]] /** Given two tuples, `(a1, ..., an)` and `(a1, ..., an)`, returns a tuple @@ -42,7 +42,7 @@ sealed trait Tuple extends Product { * tuple types has a `EmptyTuple` tail. Otherwise the result type is * `(A1, B1) *: ... *: (Ai, Bi) *: Tuple` */ - inline def zip[This >: this.type <: Tuple, T2 <: Tuple](t2: T2): Zip[This, T2] = + transparent inline def zip[This >: this.type <: Tuple, T2 <: Tuple](t2: T2): Zip[This, T2] = runtime.Tuples.zip(this, t2).asInstanceOf[Zip[This, T2]] /** Called on a tuple `(a1, ..., an)`, returns a new tuple `(f(a1), ..., f(an))`. @@ -50,27 +50,27 @@ sealed trait Tuple extends Product { * If the tuple is of the form `a1 *: ... *: Tuple` (that is, the tail is not known * to be the cons type. */ - inline def map[F[_]](f: [t] => t => F[t]): Map[this.type, F] = + transparent inline def map[F[_]](f: [t] => t => F[t]): Map[this.type, F] = runtime.Tuples.map(this, f).asInstanceOf[Map[this.type, F]] /** Given a tuple `(a1, ..., am)`, returns the tuple `(a1, ..., an)` consisting * of its first n elements. */ - inline def take[This >: this.type <: Tuple](n: Int): Take[This, n.type] = + transparent inline def take[This >: this.type <: Tuple](n: Int): Take[This, n.type] = runtime.Tuples.take(this, n).asInstanceOf[Take[This, n.type]] /** Given a tuple `(a1, ..., am)`, returns the tuple `(an+1, ..., am)` consisting * all its elements except the first n ones. */ - inline def drop[This >: this.type <: Tuple](n: Int): Drop[This, n.type] = + transparent inline def drop[This >: this.type <: Tuple](n: Int): Drop[This, n.type] = runtime.Tuples.drop(this, n).asInstanceOf[Drop[This, n.type]] /** Given a tuple `(a1, ..., am)`, returns a pair of the tuple `(a1, ..., an)` * consisting of the first n elements, and the tuple `(an+1, ..., am)` consisting * of the remaining elements. */ - inline def splitAt[This >: this.type <: Tuple](n: Int): Split[This, n.type] = + transparent inline def splitAt[This >: this.type <: Tuple](n: Int): Split[This, n.type] = runtime.Tuples.splitAt(this, n).asInstanceOf[Split[This, n.type]] } @@ -255,17 +255,17 @@ sealed trait NonEmptyTuple extends Tuple { /** Get the i-th element of this tuple. * Equivalent to productElement but with a precise return type. */ - inline def apply[This >: this.type <: NonEmptyTuple](n: Int): Elem[This, n.type] = + transparent inline def apply[This >: this.type <: NonEmptyTuple](n: Int): Elem[This, n.type] = runtime.Tuples.apply(this, n).asInstanceOf[Elem[This, n.type]] /** Get the head of this tuple */ - inline def head[This >: this.type <: NonEmptyTuple]: Head[This] = + transparent inline def head[This >: this.type <: NonEmptyTuple]: Head[This] = runtime.Tuples.apply(this, 0).asInstanceOf[Head[This]] /** Get the tail of this tuple. * This operation is O(this.size) */ - inline def tail[This >: this.type <: NonEmptyTuple]: Tail[This] = + transparent inline def tail[This >: this.type <: NonEmptyTuple]: Tail[This] = runtime.Tuples.tail(this).asInstanceOf[Tail[This]] } diff --git a/library/src/scala/compiletime/package.scala b/library/src/scala/compiletime/package.scala index ac0f03431b6e..d9cd1bae62e7 100644 --- a/library/src/scala/compiletime/package.scala +++ b/library/src/scala/compiletime/package.scala @@ -74,14 +74,14 @@ package object compiletime { * cannot be constructed from the provided type. Otherwise returns * that value wrapped in `Some`. */ - inline def constValueOpt[T]: Option[T] = + transparent inline def constValueOpt[T]: Option[T] = // implemented in dotty.tools.dotc.typer.Inliner error("Compiler bug: `constValueOpt` was not evaluated by the compiler") /** Given a constant, singleton type `T`, convert it to a value * of the same singleton type. For example: `assert(constValue[1] == 1)`. */ - inline def constValue[T]: T = + transparent inline def constValue[T]: T = // implemented in dotty.tools.dotc.typer.Inliner error("Compiler bug: `constValue` was not evaluated by the compiler") @@ -141,7 +141,7 @@ package object compiletime { * @tparam T the tuple containing the types of the values to be summoned * @return the given values typed as elements of the tuple */ - inline def summonAll[T <: Tuple]: Widen[T] = + transparent inline def summonAll[T <: Tuple]: Widen[T] = val res = inline erasedValue[T] match case _: EmptyTuple => EmptyTuple diff --git a/library/src/scala/compiletime/testing/package.scala b/library/src/scala/compiletime/testing/package.scala index 780debc87b20..0dc34ade6518 100644 --- a/library/src/scala/compiletime/testing/package.scala +++ b/library/src/scala/compiletime/testing/package.scala @@ -9,7 +9,7 @@ package testing * * The code should be a sequence of expressions or statements that may appear in a block. */ -inline def typeChecks(inline code: String): Boolean = +transparent inline def typeChecks(inline code: String): Boolean = // implemented in package dotty.tools.dotc.typer.Inliner.Intrinsics error("Compiler bug: `typeChecks` was not checked by the compiler") @@ -26,6 +26,6 @@ inline def typeChecks(inline code: String): Boolean = * * The code should be a sequence of expressions or statements that may appear in a block. */ -inline def typeCheckErrors(inline code: String): List[Error] = +transparent inline def typeCheckErrors(inline code: String): List[Error] = // implemented in package dotty.tools.dotc.typer.Inliner.Intrinsics error("Compiler bug: `typeCheckErrors` was not checked by the compiler") diff --git a/library/src/scala/quoted/ToExpr.scala b/library/src/scala/quoted/ToExpr.scala index 5d070f03eb18..461a088f13d5 100644 --- a/library/src/scala/quoted/ToExpr.scala +++ b/library/src/scala/quoted/ToExpr.scala @@ -408,8 +408,9 @@ object ToExpr { /** Default implemetation of `ToExpr[H *: T]` */ given TupleConsToExpr [H: Type: ToExpr, T <: Tuple: Type: ToExpr]: ToExpr[H *: T] with { def apply(tup: H *: T)(using Quotes): Expr[H *: T] = - '{ ${summon[ToExpr[H]].apply(tup.head)} *: ${summon[ToExpr[T]].apply(tup.tail)} } - // '{ ${Expr(tup.head)} *: ${Expr(tup.tail)} } // TODO figure out why this fails during CI documentation + val head = Expr(tup.head) + val tail = Expr(tup.tail) + '{ $head *: $tail } } /** Default implemetation of `ToExpr[BigInt]` */ diff --git a/library/src/scala/runtime/stdLibPatches/Predef.scala b/library/src/scala/runtime/stdLibPatches/Predef.scala index d811d7d2e3fb..85ebb198a515 100644 --- a/library/src/scala/runtime/stdLibPatches/Predef.scala +++ b/library/src/scala/runtime/stdLibPatches/Predef.scala @@ -3,10 +3,10 @@ package scala.runtime.stdLibPatches object Predef: import compiletime.summonFrom - inline def assert(inline assertion: Boolean, inline message: => Any): Unit = + transparent inline def assert(inline assertion: Boolean, inline message: => Any): Unit = if !assertion then scala.runtime.Scala3RunTime.assertFailed(message) - inline def assert(inline assertion: Boolean): Unit = + transparent inline def assert(inline assertion: Boolean): Unit = if !assertion then scala.runtime.Scala3RunTime.assertFailed() /** @@ -22,8 +22,8 @@ object Predef: * }}} * @group utilities */ - inline def valueOf[T]: T = summonFrom { - case ev: ValueOf[T] => ev.value + transparent inline def valueOf[T]: T = summonFrom { + case ev: ValueOf[T] => ev.value: T } /** Summon a given value of type `T`. Usually, the argument is not passed explicitly. @@ -42,6 +42,6 @@ object Predef: * * Note that `.nn` performs a checked cast, so if invoked on a null value it'll throw an NPE. */ - extension [T](x: T | Null) inline def nn: x.type & T = - scala.runtime.Scala3RunTime.nn(x) + extension [T](x: T | Null) transparent inline def nn: x.type & T = + scala.runtime.Scala3RunTime.nn(x): x.type & T end Predef diff --git a/tests/neg-macros/i7618.scala b/tests/neg-macros/i7618.scala index 3c28caf68564..b97caeb24d7d 100644 --- a/tests/neg-macros/i7618.scala +++ b/tests/neg-macros/i7618.scala @@ -31,6 +31,6 @@ object Example { val exp = Plus(Plus(Num(2), Var("x")), Num(4)) val letExp = Let("x", Num(3), exp) - Compiler.compile(letExp, Map.empty)(using Quotes.macroContext) // error // error + Compiler.compile(letExp, Map.empty)(using Quotes.macroContext) // error } } diff --git a/tests/neg-macros/i9014/Macros_1.scala b/tests/neg-macros/i9014/Macros_1.scala index 599214e3bfc2..0dcb45d513cb 100644 --- a/tests/neg-macros/i9014/Macros_1.scala +++ b/tests/neg-macros/i9014/Macros_1.scala @@ -1,4 +1,4 @@ import scala.quoted._ trait Bar -inline given Bar = ${ impl } +transparent inline given Bar = ${ impl } def impl(using Quotes): Expr[Bar] = quotes.reflect.report.throwError("Failed to expand!") diff --git a/tests/neg-macros/i9014b.check b/tests/neg-macros/i9014b.check new file mode 100644 index 000000000000..29e7846446df --- /dev/null +++ b/tests/neg-macros/i9014b.check @@ -0,0 +1,6 @@ + +-- Error: tests/neg-macros/i9014b/Test_2.scala:1:23 -------------------------------------------------------------------- +1 |val tests = summon[Bar] // error + | ^ + | Failed to expand! + | This location contains code that was inlined from Test_2.scala:1 diff --git a/tests/neg-macros/i9014b/Macros_1.scala b/tests/neg-macros/i9014b/Macros_1.scala new file mode 100644 index 000000000000..599214e3bfc2 --- /dev/null +++ b/tests/neg-macros/i9014b/Macros_1.scala @@ -0,0 +1,4 @@ +import scala.quoted._ +trait Bar +inline given Bar = ${ impl } +def impl(using Quotes): Expr[Bar] = quotes.reflect.report.throwError("Failed to expand!") diff --git a/tests/neg-macros/i9014b/Test_2.scala b/tests/neg-macros/i9014b/Test_2.scala new file mode 100644 index 000000000000..b23781947922 --- /dev/null +++ b/tests/neg-macros/i9014b/Test_2.scala @@ -0,0 +1 @@ +val tests = summon[Bar] // error diff --git a/tests/neg/cannot-reduce-summonFrom.scala b/tests/neg/cannot-reduce-summonFrom.scala index bd8592b68fa4..35e91b2dc928 100644 --- a/tests/neg/cannot-reduce-summonFrom.scala +++ b/tests/neg/cannot-reduce-summonFrom.scala @@ -1,6 +1,6 @@ object Test { - inline def bar() = + transparent inline def bar() = compiletime.summonFrom { case _: Int => } diff --git a/tests/neg/i11101.check b/tests/neg/i11101.check new file mode 100644 index 000000000000..726a1ad2a44b --- /dev/null +++ b/tests/neg/i11101.check @@ -0,0 +1,8 @@ +-- Error: tests/neg/i11101.scala:16:22 --------------------------------------------------------------------------------- +16 | summon[TC[FailHere]] // error: blow up here + | ^ + | blow up here +-- Error: tests/neg/i11101.scala:17:16 --------------------------------------------------------------------------------- +17 | TC.mkDefaultTC[FailHere] // error: blow up here + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | blow up here diff --git a/tests/neg/i11101.scala b/tests/neg/i11101.scala new file mode 100644 index 000000000000..b5e00f6a28f0 --- /dev/null +++ b/tests/neg/i11101.scala @@ -0,0 +1,17 @@ +class FailHere + +trait TC[T] +object TC { + + inline def mkDefaultTC[A]: TC[A] = inline compiletime.erasedValue[A] match { + case _: FailHere => compiletime.error("blow up here") + case _ => ??? + } + + inline given [T]: TC[T] = mkDefaultTC[T] +} + +def test = + summon[TC[Int]] // ok + summon[TC[FailHere]] // error: blow up here + TC.mkDefaultTC[FailHere] // error: blow up here diff --git a/tests/neg/i8841.check b/tests/neg/i8841.check index d5b79c6bc88b..7290982d5fdf 100644 --- a/tests/neg/i8841.check +++ b/tests/neg/i8841.check @@ -9,4 +9,7 @@ -- Error: tests/neg/i8841.scala:4:28 ----------------------------------------------------------------------------------- 4 | inline val log3: false = { println(); false } // error | ^^^^^^^^^^^^^^^^^^^^ - | inline value must be pure + | inline value must be pure: { + | println() + | false + | } diff --git a/tests/neg/i9014.check b/tests/neg/i9014.check index 629e81f6b6af..a8e21d94e501 100644 --- a/tests/neg/i9014.check +++ b/tests/neg/i9014.check @@ -1,9 +1,4 @@ -- Error: tests/neg/i9014.scala:4:25 ----------------------------------------------------------------------------------- -4 | val tests = summon[Bar] // error +4 | val tests = summon[Bar] // error: Failed to expand! | ^ - | no implicit argument of type Bar was found for parameter x of method summon in object Predef. - | I found: - | - | Bar.given_Bar - | - | But given instance given_Bar in object Bar does not match type Bar. + | Failed to expand! diff --git a/tests/neg/i9014.scala b/tests/neg/i9014.scala index 2a82d54be9d9..a31f77bba048 100644 --- a/tests/neg/i9014.scala +++ b/tests/neg/i9014.scala @@ -1,4 +1,4 @@ trait Bar object Bar: inline given Bar = compiletime.error("Failed to expand!") - val tests = summon[Bar] // error + val tests = summon[Bar] // error: Failed to expand! diff --git a/tests/neg/inlinevals-2.scala b/tests/neg/inlinevals-2.scala new file mode 100644 index 000000000000..9a3c6829c456 --- /dev/null +++ b/tests/neg/inlinevals-2.scala @@ -0,0 +1,12 @@ +object Test { + + inline def power(x: Double, inline n: Int): Double = // ok + inline if n == 0 then ??? else ??? + + inline val N = 10 + def X = 20 + + power(2.0, N) // ok, since it's a by-name parameter + power(2.0, X) // error: cannot reduce inline if + +} diff --git a/tests/neg/inlinevals.scala b/tests/neg/inlinevals.scala index d3bd16df8ac1..c4c72d128b9a 100644 --- a/tests/neg/inlinevals.scala +++ b/tests/neg/inlinevals.scala @@ -15,7 +15,7 @@ object Test { inline def bar: Int } - power(2.0, N) // ok, since it's a by-name parameter + power(2.0, N) // ok, since it's an inline parameter power(2.0, X) // error: cannot reduce inline if inline val M = X // error: rhs must be constant expression diff --git a/tests/neg/power2.scala b/tests/neg/power2.scala new file mode 100644 index 000000000000..cd7019b47b0d --- /dev/null +++ b/tests/neg/power2.scala @@ -0,0 +1,14 @@ +object Test { + + transparent inline def power(x: Double, n: Int): Double = + if (n == 0) 1.0 + else if (n == 1) x + else { + val y = power(x, n / 2) + if (n % 2 == 0) y * y else y * y * x + } + + def main(args: Array[String]): Unit = { + println(power(2.0, args.length)) // error: maximal number of inlines exceeded + } +} diff --git a/tests/pos-macros/i6588.scala b/tests/pos-macros/i6588.scala index 214dd999ff63..5497c3b287f4 100644 --- a/tests/pos-macros/i6588.scala +++ b/tests/pos-macros/i6588.scala @@ -1,6 +1,6 @@ import scala.quoted._ -inline def foo[T:Type]: Int = 10 +inline def foo[T<:AnyKind:Type]: Int = 10 def main(using Quotes) = { type S = Int diff --git a/tests/pos-macros/i7558/Macro_1.scala b/tests/pos-macros/i7558/Macro_1.scala new file mode 100644 index 000000000000..312017bbdc66 --- /dev/null +++ b/tests/pos-macros/i7558/Macro_1.scala @@ -0,0 +1,8 @@ +import scala.quoted._ + +inline def mcr(expr: => Any): Any = ${mcrImpl('expr)} + +def mcrImpl(expr: Expr[Any])(using Quotes): Expr[Any] = + import quotes.reflect._ + expr.asTerm.tpe.widen.show + expr diff --git a/tests/pos-macros/i7558/Test_2.scala b/tests/pos-macros/i7558/Test_2.scala new file mode 100644 index 000000000000..b7113215dfd2 --- /dev/null +++ b/tests/pos-macros/i7558/Test_2.scala @@ -0,0 +1 @@ +def eq[T](p: T) = mcr(p) diff --git a/tests/pos-macros/i8858/Macro_1.scala b/tests/pos-macros/i8858/Macro_1.scala new file mode 100644 index 000000000000..8eb0182c2779 --- /dev/null +++ b/tests/pos-macros/i8858/Macro_1.scala @@ -0,0 +1,9 @@ +import scala.quoted._ + +inline def mcr(inline x: Any): Any = ${ mcrImpl('x) } +def mcrImpl(expr: Expr[Any])(using Quotes): Expr[Any] = + import quotes.reflect._ + expr.asTerm match + case Inlined(_, _, id1) => + println(id1.tpe.widen.show) + '{()} diff --git a/tests/pos-macros/i8858/Test_2.scala b/tests/pos-macros/i8858/Test_2.scala new file mode 100644 index 000000000000..21acefdd2ccc --- /dev/null +++ b/tests/pos-macros/i8858/Test_2.scala @@ -0,0 +1,3 @@ +trait Foo[A] + +def check[T](x: Foo[T]) = mcr(x) diff --git a/tests/pos-macros/i9296/Test_2.scala b/tests/pos-macros/i9296/Test_2.scala index 8e22c5ae841b..7ee26cc869cf 100644 --- a/tests/pos-macros/i9296/Test_2.scala +++ b/tests/pos-macros/i9296/Test_2.scala @@ -5,10 +5,11 @@ import scala.concurrent._ trait CB[T] -given myConversion[T]: Conversion[Future[T],CB[T]] = (ft => ???) object O { + given myConversion[T]: Conversion[Future[T],CB[T]] = (ft => ???) + def main(argvs: Array[String]): Unit = { val p = Promise[Int]() //val cbp = summon[Conversion[Future[Int],CB[Int]]] //works diff --git a/tests/pos/i10107c.scala b/tests/pos/i10107c.scala new file mode 100644 index 000000000000..b42e62ddb7dc --- /dev/null +++ b/tests/pos/i10107c.scala @@ -0,0 +1,12 @@ +import scala.quoted._ + +inline def isTrue: Boolean = true + +inline def oneOf(): String = { + inline if isTrue then + "foo" + else + "bar" +} + +def test1 = oneOf() diff --git a/tests/pos/i10295.scala b/tests/pos/i10295.scala index 8fc237e2e853..d33a67ad799e 100644 --- a/tests/pos/i10295.scala +++ b/tests/pos/i10295.scala @@ -3,7 +3,7 @@ trait M: object X: def foo(): X = ??? -inline def m(using m: M): m.type = m +transparent inline def m(using m: M): m.type = m def doSomething(body: M ?=> Unit) = body(using new M{}) diff --git a/tests/pos/i11184a.scala b/tests/pos/i11184a.scala new file mode 100644 index 000000000000..b4b0beca45ee --- /dev/null +++ b/tests/pos/i11184a.scala @@ -0,0 +1,5 @@ +import scala.quoted._ + +inline def isTrue: Boolean = true +inline def oneOf: String = inline if isTrue then "foo" else "bar" +def test1 = oneOf diff --git a/tests/pos/i11184b.scala b/tests/pos/i11184b.scala new file mode 100644 index 000000000000..16a12b92d15b --- /dev/null +++ b/tests/pos/i11184b.scala @@ -0,0 +1,5 @@ +import scala.quoted._ + +inline def isTrue(): Boolean = true +inline def oneOf: String = inline if isTrue() then "foo" else "bar" +def test1 = oneOf diff --git a/tests/pos/i11184c.scala b/tests/pos/i11184c.scala new file mode 100644 index 000000000000..bcd83fb056a2 --- /dev/null +++ b/tests/pos/i11184c.scala @@ -0,0 +1,6 @@ +import scala.quoted._ + +object Foo: + inline def isTrue: Boolean = true +inline def oneOf: String = inline if Foo.isTrue then "foo" else "bar" +def test1 = oneOf diff --git a/tests/pos/i8892.scala b/tests/pos/i8892.scala index f08b12341d3b..0a7e7223fe06 100644 --- a/tests/pos/i8892.scala +++ b/tests/pos/i8892.scala @@ -6,7 +6,7 @@ class Dummy extends Reporter: object ABug { sealed trait Nat { - inline def ++ : Succ[this.type] = Succ(this) + transparent inline def ++ : Succ[this.type] = Succ(this) transparent inline def +(inline that: Nat): Nat = inline this match { diff --git a/tests/pos/inline-separate/A_1.scala b/tests/pos/inline-separate/A_1.scala index 72783578df80..e5055f1761e4 100644 --- a/tests/pos/inline-separate/A_1.scala +++ b/tests/pos/inline-separate/A_1.scala @@ -1,5 +1,5 @@ object A { - inline def summon[T] = compiletime.summonFrom { + transparent inline def summon[T] = compiletime.summonFrom { case t: T => t } } diff --git a/tests/pos/inline-separate/Test_2.scala b/tests/pos/inline-separate/Test_2.scala index 2a7c012b3771..ead2408dd407 100644 --- a/tests/pos/inline-separate/Test_2.scala +++ b/tests/pos/inline-separate/Test_2.scala @@ -1,7 +1,7 @@ import A._ object Test extends App { class Foo(f: => Foo) - inline implicit def foo(implicit f: => Foo): Foo = new Foo(summon[Foo]) + transparent inline implicit def foo(implicit f: => Foo): Foo = new Foo(summon[Foo]) def summonFoo(implicit ev: Foo): Foo = ev summonFoo } diff --git a/tests/pos/summonInline.scala b/tests/pos/summonInline.scala new file mode 100644 index 000000000000..e007fd214981 --- /dev/null +++ b/tests/pos/summonInline.scala @@ -0,0 +1,10 @@ +import scala.compiletime.summonInline + +type A +given A = ??? + +inline def summon1: A = summonInline[A] +transparent inline def summon2: A = summonInline[A] + +def test1 = summon1 +def test2 = summon2 diff --git a/tests/run-custom-args/Yretain-trees/tasty-definitions-3.check b/tests/run-custom-args/Yretain-trees/tasty-definitions-3.check index 3cdc1b6b29f7..3c2b67edfde0 100644 --- a/tests/run-custom-args/Yretain-trees/tasty-definitions-3.check +++ b/tests/run-custom-args/Yretain-trees/tasty-definitions-3.check @@ -1,3 +1,3 @@ -DefDef("foo", Nil, Inferred(), None) -ValDef("bar", Inferred(), None) +DefDef("foo", Nil, TypeIdent("Int"), Some(Apply(Select(Literal(IntConstant(1)), "+"), List(Literal(IntConstant(2)))))) +ValDef("bar", TypeIdent("Int"), Some(Apply(Select(Literal(IntConstant(2)), "+"), List(Literal(IntConstant(3)))))) Bind("x", Ident("_")) diff --git a/tests/run-custom-args/Yretain-trees/tasty-load-tree-2.check b/tests/run-custom-args/Yretain-trees/tasty-load-tree-2.check index 70f474a9e253..e66a336fb836 100644 --- a/tests/run-custom-args/Yretain-trees/tasty-load-tree-2.check +++ b/tests/run-custom-args/Yretain-trees/tasty-load-tree-2.check @@ -1,2 +1,2 @@ -DefDef("foo", Nil, Inferred(), None) -ValDef("bar", Inferred(), None) +DefDef("foo", Nil, TypeIdent("Int"), Some(Apply(Select(Literal(IntConstant(1)), "+"), List(Literal(IntConstant(2)))))) +ValDef("bar", TypeIdent("Int"), Some(Apply(Select(Literal(IntConstant(2)), "+"), List(Literal(IntConstant(3)))))) diff --git a/tests/run-macros/beta-reduce-inline-result.check b/tests/run-macros/beta-reduce-inline-result.check index f1c651a532ba..ed716e4990d8 100644 --- a/tests/run-macros/beta-reduce-inline-result.check +++ b/tests/run-macros/beta-reduce-inline-result.check @@ -1,6 +1,6 @@ -compile-time: 4:Int +compile-time: Macros.betaReduce[Int, Int](Test.dummy1)(3) run-time: 4 -compile-time: 1:Int +compile-time: Macros.betaReduce[Int, Int](Test.dummy2)(1) run-time: 1 run-time: 5 run-time: 7 diff --git a/tests/run-macros/i10043/Macro_1.scala b/tests/run-macros/i10043/Macro_1.scala new file mode 100644 index 000000000000..0a8f6b17e714 --- /dev/null +++ b/tests/run-macros/i10043/Macro_1.scala @@ -0,0 +1,13 @@ +package example + +import scala.quoted._ + +trait Trait { + final val foo = 23 +} + +object Trait { + inline def get: Trait = ${ getImpl } + + def getImpl(using Quotes): Expr[Trait] = '{ new Trait {} } +} diff --git a/tests/run-macros/i10043/Test_2.scala b/tests/run-macros/i10043/Test_2.scala new file mode 100644 index 000000000000..c4634cc44dc3 --- /dev/null +++ b/tests/run-macros/i10043/Test_2.scala @@ -0,0 +1 @@ +@main def Test = println(example.Trait.get) diff --git a/tests/run-macros/i7025b/Macros_1.scala b/tests/run-macros/i7025b/Macros_1.scala new file mode 100644 index 000000000000..e2207a3ee893 --- /dev/null +++ b/tests/run-macros/i7025b/Macros_1.scala @@ -0,0 +1,21 @@ +inline def debug: Unit = ${Macros.debugImpl} + +object Macros { + import scala.quoted._ + + def debugImpl(using Quotes): Expr[Unit] = { + import quotes.reflect._ + + def nearestEnclosingDef(owner: Symbol): Symbol = + if owner.isDefDef then owner + else if owner.isClassDef then owner + else nearestEnclosingDef(owner.owner) + + val sym = nearestEnclosingDef(Symbol.spliceOwner) + if sym.isDefDef then + val code = sym.signature.toString + '{ println(${Expr(code)}) } + else + '{()} + } +} diff --git a/tests/run-macros/i7025b/Test_2.scala b/tests/run-macros/i7025b/Test_2.scala new file mode 100644 index 000000000000..d59038c17823 --- /dev/null +++ b/tests/run-macros/i7025b/Test_2.scala @@ -0,0 +1,9 @@ +object Test { + def main(args: Array[String]): Unit = { + bar("world", 100, true) + } + + def bar(a1: String, a2: Long, a3: Boolean) = { + debug + } +} diff --git a/tests/run-macros/i8877/Macros_1.scala b/tests/run-macros/i8877/Macros_1.scala new file mode 100644 index 000000000000..92eb01938455 --- /dev/null +++ b/tests/run-macros/i8877/Macros_1.scala @@ -0,0 +1,20 @@ +//macros +import scala.quoted._ + +object Macros { + //a specialization of the `findOwner` function from `sourcecode` for our purposes + private def firstNonSyntheticOwner(using Quotes)(s: quotes.reflect.Symbol): quotes.reflect.Symbol = { + import quotes.reflect._ + if (s.flags.is(Flags.Synthetic)) firstNonSyntheticOwner(s.owner) + else s + } + + def genOwnerImpl()(using Quotes): Expr[String] = { + import quotes.reflect._ + Expr(firstNonSyntheticOwner(Symbol.spliceOwner).name) + } +} + +object Foo { + inline def genOwner: String = ${ Macros.genOwnerImpl() } +} diff --git a/tests/run-macros/i8877/Test_2.scala b/tests/run-macros/i8877/Test_2.scala new file mode 100644 index 000000000000..032efb5f0e6c --- /dev/null +++ b/tests/run-macros/i8877/Test_2.scala @@ -0,0 +1,5 @@ + +// main +@main def Test = { + println(Foo.genOwner) +} \ No newline at end of file diff --git a/tests/run-macros/no-symbol/2.scala b/tests/run-macros/no-symbol/2.scala index 0e9357d2cc20..cb85878e7bd9 100644 --- a/tests/run-macros/no-symbol/2.scala +++ b/tests/run-macros/no-symbol/2.scala @@ -1,6 +1,6 @@ object Test { def main(args: Array[String]): Unit = { assert(Macro.foo[Foo] == "symbol") - assert(Macro.foo[Box] == "no symbol") + assert(Macro.foo[Box[_]] == "no symbol") } } diff --git a/tests/run-macros/reflect-inline/assert_1.scala b/tests/run-macros/reflect-inline/assert_1.scala index d6b115529ea6..1255fc826a62 100644 --- a/tests/run-macros/reflect-inline/assert_1.scala +++ b/tests/run-macros/reflect-inline/assert_1.scala @@ -1,13 +1,13 @@ import scala.quoted._ object api { - extension (inline x: String) inline def stripMargin: String = + extension (inline x: String) transparent inline def stripMargin: String = ${ stripImpl('x) } private def stripImpl(x: Expr[String])(using Quotes): Expr[String] = Expr(augmentString(x.valueOrError).stripMargin) - inline def typeChecks(inline x: String): Boolean = + transparent inline def typeChecks(inline x: String): Boolean = ${ typeChecksImpl('{scala.compiletime.testing.typeChecks(x)}) } private def typeChecksImpl(b: Expr[Boolean])(using Quotes): Expr[Boolean] = { diff --git a/tests/run-macros/reflect-sourceCode.check b/tests/run-macros/reflect-sourceCode.check new file mode 100644 index 000000000000..e4e8e81b1acb --- /dev/null +++ b/tests/run-macros/reflect-sourceCode.check @@ -0,0 +1,7 @@ +args(0) +args( 0 ) +args( 0 /* ignore */) +f +f +{ f } +{ f; f } diff --git a/tests/run-macros/reflect-sourceCode/Test_2.scala b/tests/run-macros/reflect-sourceCode/Test_2.scala index a1a6d6562473..8bd0b6317066 100644 --- a/tests/run-macros/reflect-sourceCode/Test_2.scala +++ b/tests/run-macros/reflect-sourceCode/Test_2.scala @@ -4,12 +4,12 @@ object Test { def f(implicit x: Int): Int = x * x def main(args: Array[String]): Unit = { implicit val x: Int = 10 - assert(args(0).reflect == "args(0)") - assert(args( 0 ).reflect == "args( 0 )") - assert(args( 0 /* ignore */).reflect == "args( 0 /* ignore */)") - assert(f.reflect == "f") - assert((f).reflect == "f") - assert( { f }.reflect == "{ f }") - assert( { f; f }.reflect == "{ f; f }") + println(args(0).reflect) + println(args( 0 ).reflect) + println(args( 0 /* ignore */).reflect) + println(f.reflect) + println((f).reflect) + println( { f }.reflect) + println( { f; f }.reflect) } } diff --git a/tests/run-macros/reflect-typeChecks/assert_1.scala b/tests/run-macros/reflect-typeChecks/assert_1.scala index db8d4fe3dd31..b7456ba6c6e8 100644 --- a/tests/run-macros/reflect-typeChecks/assert_1.scala +++ b/tests/run-macros/reflect-typeChecks/assert_1.scala @@ -2,8 +2,8 @@ import scala.quoted._ object scalatest { - inline def assertCompile(inline code: String): Unit = ${ assertImpl('code, '{compiletime.testing.typeChecks(code)}, true) } - inline def assertNotCompile(inline code: String): Unit = ${ assertImpl('code, '{compiletime.testing.typeChecks(code)}, false) } + transparent inline def assertCompile(inline code: String): Unit = ${ assertImpl('code, '{compiletime.testing.typeChecks(code)}, true) } + transparent inline def assertNotCompile(inline code: String): Unit = ${ assertImpl('code, '{compiletime.testing.typeChecks(code)}, false) } def assertImpl(code: Expr[String], actual: Expr[Boolean], expect: Boolean)(using Quotes) : Expr[Unit] = { '{ assert(${Expr(expect)} == $actual) } diff --git a/tests/run-macros/string-context-implicits/Macro_1.scala b/tests/run-macros/string-context-implicits/Macro_1.scala index b0049f9049d3..0e1b3baa7956 100644 --- a/tests/run-macros/string-context-implicits/Macro_1.scala +++ b/tests/run-macros/string-context-implicits/Macro_1.scala @@ -26,5 +26,6 @@ trait Show[-T] { def show(x: T): String } -given Show[Int] = x => s"Int($x)" -given Show[String] = x => s"Str($x)" +object Show: + given Show[Int] = x => s"Int($x)" + given Show[String] = x => s"Str($x)" diff --git a/tests/run-macros/tasty-extractors-2.check b/tests/run-macros/tasty-extractors-2.check index a5a3c806c923..3fa60370e850 100644 --- a/tests/run-macros/tasty-extractors-2.check +++ b/tests/run-macros/tasty-extractors-2.check @@ -49,7 +49,7 @@ TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Unit") Inlined(None, Nil, Block(List(ClassDef("Foo", DefDef("", List(TermParamClause(Nil)), Inferred(), None), List(Apply(Select(New(Inferred()), ""), Nil)), Nil, None, List(DefDef("a", Nil, Inferred(), Some(Literal(IntConstant(0))))))), Literal(UnitConstant()))) TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Unit") -Inlined(None, Nil, Block(List(ClassDef("Foo", DefDef("", List(TermParamClause(Nil)), Inferred(), None), List(Apply(Select(New(Inferred()), ""), Nil), TypeSelect(Select(Ident("_root_"), "scala"), "Product"), TypeSelect(Select(Ident("_root_"), "scala"), "Serializable")), Nil, None, List(DefDef("copy", List(TermParamClause(Nil)), Inferred(), Some(Apply(Select(New(Inferred()), ""), Nil))))), ValDef("Foo", TypeIdent("Foo$"), Some(Apply(Select(New(TypeIdent("Foo$")), ""), Nil))), ClassDef("Foo$", DefDef("", List(TermParamClause(Nil)), Inferred(), None), List(Apply(Select(New(Inferred()), ""), Nil)), Nil, Some(ValDef("_", Singleton(Ident("Foo")), None)), List(DefDef("apply", List(TermParamClause(Nil)), Inferred(), Some(Apply(Select(New(Inferred()), ""), Nil))), DefDef("unapply", List(TermParamClause(List(ValDef("x$1", Inferred(), None)))), Singleton(Literal(BooleanConstant(true))), Some(Literal(BooleanConstant(true)))), DefDef("toString", Nil, Inferred(), Some(Literal(StringConstant("Foo"))))))), Literal(UnitConstant()))) +Inlined(None, Nil, Block(List(ClassDef("Foo", DefDef("", List(TermParamClause(Nil)), Inferred(), None), List(Apply(Select(New(Inferred()), ""), Nil), TypeSelect(Select(Ident("_root_"), "scala"), "Product"), TypeSelect(Select(Ident("_root_"), "scala"), "Serializable")), Nil, None, List(DefDef("hashCode", List(TermParamClause(Nil)), Inferred(), Some(Apply(Ident("_hashCode"), List(This(Some("Foo")))))), DefDef("equals", List(TermParamClause(List(ValDef("x$0", Inferred(), None)))), Inferred(), Some(Apply(Select(Apply(Select(This(Some("Foo")), "eq"), List(TypeApply(Select(Ident("x$0"), "$asInstanceOf$"), List(Inferred())))), "||"), List(Match(Ident("x$0"), List(CaseDef(Bind("x$0", Typed(Ident("_"), Inferred())), None, Apply(Select(Literal(BooleanConstant(true)), "&&"), List(Apply(Select(Ident("x$0"), "canEqual"), List(This(Some("Foo"))))))), CaseDef(Ident("_"), None, Literal(BooleanConstant(false))))))))), DefDef("toString", List(TermParamClause(Nil)), Inferred(), Some(Apply(Ident("_toString"), List(This(Some("Foo")))))), DefDef("canEqual", List(TermParamClause(List(ValDef("that", Inferred(), None)))), Inferred(), Some(TypeApply(Select(Ident("that"), "isInstanceOf"), List(Inferred())))), DefDef("productArity", Nil, Inferred(), Some(Literal(IntConstant(0)))), DefDef("productPrefix", Nil, Inferred(), Some(Literal(StringConstant("Foo")))), DefDef("productElement", List(TermParamClause(List(ValDef("n", Inferred(), None)))), Inferred(), Some(Match(Ident("n"), List(CaseDef(Ident("_"), None, Apply(Ident("throw"), List(Apply(Select(New(Inferred()), ""), List(Apply(Select(Ident("n"), "toString"), Nil)))))))))), DefDef("productElementName", List(TermParamClause(List(ValDef("n", Inferred(), None)))), Inferred(), Some(Match(Ident("n"), List(CaseDef(Ident("_"), None, Apply(Ident("throw"), List(Apply(Select(New(Inferred()), ""), List(Apply(Select(Ident("n"), "toString"), Nil)))))))))), DefDef("copy", List(TermParamClause(Nil)), Inferred(), Some(Apply(Select(New(Inferred()), ""), Nil))))), ValDef("Foo", TypeIdent("Foo$"), Some(Apply(Select(New(TypeIdent("Foo$")), ""), Nil))), ClassDef("Foo$", DefDef("", List(TermParamClause(Nil)), Inferred(), None), List(Apply(Select(New(Inferred()), ""), Nil), Inferred()), Nil, Some(ValDef("_", Singleton(Ident("Foo")), None)), List(DefDef("apply", List(TermParamClause(Nil)), Inferred(), Some(Apply(Select(New(Inferred()), ""), Nil))), DefDef("unapply", List(TermParamClause(List(ValDef("x$1", Inferred(), None)))), Singleton(Literal(BooleanConstant(true))), Some(Literal(BooleanConstant(true)))), DefDef("toString", Nil, Inferred(), Some(Literal(StringConstant("Foo")))), TypeDef("MirroredMonoType", TypeBoundsTree(Inferred(), Inferred())), DefDef("fromProduct", List(TermParamClause(List(ValDef("x$0", Inferred(), None)))), Inferred(), Some(Apply(Select(New(Inferred()), ""), Nil)))))), Literal(UnitConstant()))) TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Unit") Inlined(None, Nil, Block(List(ClassDef("Foo1", DefDef("", List(TermParamClause(List(ValDef("a", TypeIdent("Int"), None)))), Inferred(), None), List(Apply(Select(New(Inferred()), ""), Nil)), Nil, None, List(ValDef("a", Inferred(), None)))), Literal(UnitConstant()))) diff --git a/tests/run-macros/whitebox-inline-macro/Macro_1.scala b/tests/run-macros/whitebox-inline-macro/Macro_1.scala new file mode 100644 index 000000000000..bac54fe3301c --- /dev/null +++ b/tests/run-macros/whitebox-inline-macro/Macro_1.scala @@ -0,0 +1,11 @@ +import scala.quoted._ + +object Macros { + + inline def blackbox: Int = ${one} + + transparent inline def whitebox: Int = ${one} + + private def one(using Quotes): Expr[Int] = Expr(1) + +} diff --git a/tests/run-macros/whitebox-inline-macro/Test_2.scala b/tests/run-macros/whitebox-inline-macro/Test_2.scala new file mode 100644 index 000000000000..07f6bea5319b --- /dev/null +++ b/tests/run-macros/whitebox-inline-macro/Test_2.scala @@ -0,0 +1,12 @@ +import scala.quoted._ +import Macros._ + +object Test { + def main(args: Array[String]): Unit = { + val a: Int = blackbox + val b: 1 = whitebox + + assert(a == 1) + assert(b == 1) + } +} diff --git a/tests/run-staging/i11184.scala b/tests/run-staging/i11184.scala new file mode 100644 index 000000000000..5ef3e2a037a7 --- /dev/null +++ b/tests/run-staging/i11184.scala @@ -0,0 +1,22 @@ +import scala.quoted._ + +object Test { + def main(args: Array[String]): Unit = { + given staging.Compiler = staging.Compiler.make(getClass.getClassLoader) + staging.run(genCode) + } +} + +inline def isTrue1: Boolean = true + +object Foo: + inline def isTrue2: Boolean = true + +inline def isTrue3(): Boolean = true + +inline def oneOf: String = + inline if isTrue1 then "foo" else "bar" + inline if Foo.isTrue2 then "foo" else "bar" + inline if isTrue3() then "foo" else "bar" + +def genCode(using Quotes) = '{ oneOf } diff --git a/tests/run/i9011.scala b/tests/run/i9011.scala index c75871a865bb..3fa3a0979372 100644 --- a/tests/run/i9011.scala +++ b/tests/run/i9011.scala @@ -14,7 +14,7 @@ object Eq { def eqv(x: Int, y: Int) = x == y } - inline def summonAll[T <: Tuple]: List[Eq[_]] = inline erasedValue[T] match { + transparent inline def summonAll[T <: Tuple]: List[Eq[_]] = inline erasedValue[T] match { case _: EmptyTuple => Nil case _: (t *: ts) => summonInline[Eq[t]] :: summonAll[ts] } @@ -40,7 +40,7 @@ object Eq { } } - inline given derived[T](using m: Mirror.Of[T]): Eq[T] = { + transparent inline given derived[T](using m: Mirror.Of[T]): Eq[T] = { val elemInstances = summonAll[m.MirroredElemTypes] inline m match { case s: Mirror.SumOf[T] => eqSum(s, elemInstances) diff --git a/tests/run/typeclass-derivation2b.scala b/tests/run/typeclass-derivation2b.scala index 704e8cb2cc9b..08758ceb8a84 100644 --- a/tests/run/typeclass-derivation2b.scala +++ b/tests/run/typeclass-derivation2b.scala @@ -94,9 +94,9 @@ object Eq { import scala.compiletime.{erasedValue, summonInline} import TypeLevel._ - inline def tryEql[T](x: T, y: T) = summonInline[Eq[T]].eql(x, y) + transparent inline def tryEql[T](x: T, y: T) = summonInline[Eq[T]].eql(x, y) - inline def eqlElems[Elems <: Tuple](x: Product, y: Product, n: Int): Boolean = + transparent inline def eqlElems[Elems <: Tuple](x: Product, y: Product, n: Int): Boolean = inline erasedValue[Elems] match { case _: (elem *: elems1) => tryEql[elem]( @@ -107,7 +107,7 @@ object Eq { true } - inline def eqlCases[T, Alts <: Tuple](x: T, y: T, genSum: GenericSum[T], ord: Int, inline n: Int): Boolean = + transparent inline def eqlCases[T, Alts <: Tuple](x: T, y: T, genSum: GenericSum[T], ord: Int, inline n: Int): Boolean = inline erasedValue[Alts] match { case _: (alt *: alts1) => if (ord == n) @@ -123,7 +123,7 @@ object Eq { false } - inline def derived[T](implicit ev: Generic[T]): Eq[T] = new Eq[T] { + transparent inline def derived[T](implicit ev: Generic[T]): Eq[T] = new Eq[T] { def eql(x: T, y: T): Boolean = { inline ev match { case evv: GenericSum[T] => diff --git a/tests/run/typeclass-derivation3.scala b/tests/run/typeclass-derivation3.scala index 675faab8da5d..e8ea1ec16d3b 100644 --- a/tests/run/typeclass-derivation3.scala +++ b/tests/run/typeclass-derivation3.scala @@ -35,11 +35,11 @@ object typeclasses { import compiletime._ import scala.deriving._ - inline def tryEql[TT](x: TT, y: TT): Boolean = summonFrom { + transparent inline def tryEql[TT](x: TT, y: TT): Boolean = summonFrom { case eq: Eq[TT] => eq.eql(x, y) } - inline def eqlElems[Elems <: Tuple](n: Int)(x: Product, y: Product): Boolean = + transparent inline def eqlElems[Elems <: Tuple](n: Int)(x: Product, y: Product): Boolean = inline erasedValue[Elems] match { case _: (elem *: elems1) => tryEql[elem](x.productElement(n).asInstanceOf[elem], y.productElement(n).asInstanceOf[elem]) && @@ -48,7 +48,7 @@ object typeclasses { true } - inline def eqlCases[Alts](n: Int)(x: Any, y: Any, ord: Int): Boolean = + transparent inline def eqlCases[Alts](n: Int)(x: Any, y: Any, ord: Int): Boolean = inline erasedValue[Alts] match { case _: (alt *: alts1) => if (ord == n) @@ -61,7 +61,7 @@ object typeclasses { false } - inline def derived[T](implicit ev: Mirror.Of[T]): Eq[T] = new Eq[T] { + transparent inline def derived[T](implicit ev: Mirror.Of[T]): Eq[T] = new Eq[T] { def eql(x: T, y: T): Boolean = inline ev match { case m: Mirror.SumOf[T] => @@ -90,11 +90,11 @@ object typeclasses { def nextInt(buf: mutable.ListBuffer[Int]): Int = try buf.head finally buf.trimStart(1) - inline def tryPickle[T](buf: mutable.ListBuffer[Int], x: T): Unit = summonFrom { + transparent inline def tryPickle[T](buf: mutable.ListBuffer[Int], x: T): Unit = summonFrom { case pkl: Pickler[T] => pkl.pickle(buf, x) } - inline def pickleElems[Elems <: Tuple](n: Int)(buf: mutable.ListBuffer[Int], x: Product): Unit = + transparent inline def pickleElems[Elems <: Tuple](n: Int)(buf: mutable.ListBuffer[Int], x: Product): Unit = inline erasedValue[Elems] match { case _: (elem *: elems1) => tryPickle[elem](buf, x.productElement(n).asInstanceOf[elem]) @@ -102,7 +102,7 @@ object typeclasses { case _: EmptyTuple => } - inline def pickleCases[Alts <: Tuple](n: Int)(buf: mutable.ListBuffer[Int], x: Any, ord: Int): Unit = + transparent inline def pickleCases[Alts <: Tuple](n: Int)(buf: mutable.ListBuffer[Int], x: Any, ord: Int): Unit = inline erasedValue[Alts] match { case _: (alt *: alts1) => if (ord == n) @@ -113,11 +113,11 @@ object typeclasses { case _: EmptyTuple => } - inline def tryUnpickle[T](buf: mutable.ListBuffer[Int]): T = summonFrom { + transparent inline def tryUnpickle[T](buf: mutable.ListBuffer[Int]): T = summonFrom { case pkl: Pickler[T] => pkl.unpickle(buf) } - inline def unpickleElems[Elems <: Tuple](n: Int)(buf: mutable.ListBuffer[Int], elems: Array[Any]): Unit = + transparent inline def unpickleElems[Elems <: Tuple](n: Int)(buf: mutable.ListBuffer[Int], elems: Array[Any]): Unit = inline erasedValue[Elems] match { case _: (elem *: elems1) => elems(n) = tryUnpickle[elem](buf) @@ -125,7 +125,7 @@ object typeclasses { case _: EmptyTuple => } - inline def unpickleCase[T, Elems <: Tuple](buf: mutable.ListBuffer[Int], m: Mirror.ProductOf[T]): T = { + transparent inline def unpickleCase[T, Elems <: Tuple](buf: mutable.ListBuffer[Int], m: Mirror.ProductOf[T]): T = { inline val size = constValue[Tuple.Size[Elems]] inline if (size == 0) m.fromProduct(EmptyTuple) @@ -140,7 +140,7 @@ object typeclasses { } } - inline def unpickleCases[T, Alts <: Tuple](n: Int)(buf: mutable.ListBuffer[Int], ord: Int): T = + transparent inline def unpickleCases[T, Alts <: Tuple](n: Int)(buf: mutable.ListBuffer[Int], ord: Int): T = inline erasedValue[Alts] match { case _: (alt *: alts1) => if (ord == n) @@ -153,7 +153,7 @@ object typeclasses { throw new IndexOutOfBoundsException(s"unexpected ordinal number: $ord") } - inline def derived[T](implicit ev: Mirror.Of[T]): Pickler[T] = new { + transparent inline def derived[T](implicit ev: Mirror.Of[T]): Pickler[T] = new { def pickle(buf: mutable.ListBuffer[Int], x: T): Unit = inline ev match { case m: Mirror.SumOf[T] => @@ -188,9 +188,9 @@ object typeclasses { import compiletime._ import deriving._ - inline def tryShow[T](x: T): String = summonInline[Show[T]].show(x) + transparent inline def tryShow[T](x: T): String = summonInline[Show[T]].show(x) - inline def showElems[Elems <: Tuple, Labels <: Tuple](n: Int)(x: Product): List[String] = + transparent inline def showElems[Elems <: Tuple, Labels <: Tuple](n: Int)(x: Product): List[String] = inline erasedValue[Elems] match { case _: (elem *: elems1) => inline erasedValue[Labels] match { @@ -203,7 +203,7 @@ object typeclasses { Nil } - inline def showCase[T](x: Any, m: Mirror.ProductOf[T]): String = { + transparent inline def showCase[T](x: Any, m: Mirror.ProductOf[T]): String = { val label = constValue[m.MirroredLabel] inline m match { case m: Mirror.Singleton => label @@ -211,7 +211,7 @@ object typeclasses { } } - inline def showCases[Alts <: Tuple](n: Int)(x: Any, ord: Int): String = + transparent inline def showCases[Alts <: Tuple](n: Int)(x: Any, ord: Int): String = inline erasedValue[Alts] match { case _: (alt *: alts1) => if (ord == n) showCase(x, summonInline[Mirror.ProductOf[`alt`]]) @@ -220,7 +220,7 @@ object typeclasses { throw new MatchError(x) } - inline def derived[T](implicit ev: Mirror.Of[T]): Show[T] = new { + transparent inline def derived[T](implicit ev: Mirror.Of[T]): Show[T] = new { def show(x: T): String = inline ev match { case m: Mirror.SumOf[T] => diff --git a/tests/run/whitebox-inline.scala b/tests/run/whitebox-inline.scala new file mode 100644 index 000000000000..09e7911caaa1 --- /dev/null +++ b/tests/run/whitebox-inline.scala @@ -0,0 +1,16 @@ +import scala.quoted._ + +object Test { + def main(args: Array[String]): Unit = { + val a: Int = blackbox + val b: 1 = whitebox + + assert(a == 1) + assert(b == 1) + } + + inline def blackbox: Int = 1 + + transparent inline def whitebox: Int = 1 + +} From b7034062a65d60512de22a14390cf8495d1ce135 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 21 Jan 2021 18:08:41 +0100 Subject: [PATCH 02/56] Force inlining in conditions of `inline if`s --- compiler/src/dotty/tools/dotc/core/Mode.scala | 5 +++++ compiler/src/dotty/tools/dotc/typer/Inliner.scala | 8 ++++---- tests/neg-macros/i9014.check | 8 ++------ tests/neg-macros/i9014/Macros_1.scala | 2 +- tests/neg-macros/i9014b.check | 8 ++++++-- tests/neg-macros/i9014b/Macros_1.scala | 2 +- tests/neg/cannot-reduce-summonFrom.scala | 2 +- tests/pos/i11184d.scala | 5 +++++ tests/run/i9011.scala | 4 ++-- tests/run/typeclass-derivation2b.scala | 8 ++++---- 10 files changed, 31 insertions(+), 21 deletions(-) create mode 100644 tests/pos/i11184d.scala diff --git a/compiler/src/dotty/tools/dotc/core/Mode.scala b/compiler/src/dotty/tools/dotc/core/Mode.scala index 864fcd79fe69..a42493fad2f1 100644 --- a/compiler/src/dotty/tools/dotc/core/Mode.scala +++ b/compiler/src/dotty/tools/dotc/core/Mode.scala @@ -122,4 +122,9 @@ object Mode { /** Are we enforcing null safety */ val SafeNulls = newMode(28, "SafeNulls") + + /** We are typing the body of the condition of an `inline if` or the scrutinee of an `inline match` + * This mode forces expansion of inline calls in those positions even during typing. + */ + val ForceInline: Mode = newMode(29, "ForceInline") } diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 45af241ef36d..fc021b754e3a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -1253,7 +1253,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) { } override def typedIf(tree: untpd.If, pt: Type)(using Context): Tree = - typed(tree.cond, defn.BooleanType) match { + typed(tree.cond, defn.BooleanType)(using ctx.addMode(Mode.ForceInline)) match { case cond1 @ ConstantValue(b: Boolean) => val selected0 = if (b) tree.thenp else tree.elsep val selected = if (selected0.isEmpty) tpd.Literal(Constant(())) else typed(selected0, pt) @@ -1343,9 +1343,9 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) { !suppressInline && !tree.tpe.widenTermRefExpr.isInstanceOf[MethodOrPoly] && Inliner.isInlineable(tree) && - StagingContext.level == 0 - then - Inliner.inlineCall(tree) + StagingContext.level == 0 && + (ctx.isAfterTyper || tree.symbol.is(Transparent) || ctx.mode.is(Mode.ForceInline) || ctx.settings.YinlineBlackboxWhileTyping.value) + then Inliner.inlineCall(tree) else tree } diff --git a/tests/neg-macros/i9014.check b/tests/neg-macros/i9014.check index be338d620c60..509eac067fc8 100644 --- a/tests/neg-macros/i9014.check +++ b/tests/neg-macros/i9014.check @@ -2,9 +2,5 @@ -- Error: tests/neg-macros/i9014/Test_2.scala:1:23 --------------------------------------------------------------------- 1 |val tests = summon[Bar] // error | ^ - | no implicit argument of type Bar was found for parameter x of method summon in object Predef. - | I found: - | - | given_Bar - | - | But given instance given_Bar does not match type Bar. + | Failed to expand! + | This location contains code that was inlined from Test_2.scala:1 diff --git a/tests/neg-macros/i9014/Macros_1.scala b/tests/neg-macros/i9014/Macros_1.scala index 0dcb45d513cb..599214e3bfc2 100644 --- a/tests/neg-macros/i9014/Macros_1.scala +++ b/tests/neg-macros/i9014/Macros_1.scala @@ -1,4 +1,4 @@ import scala.quoted._ trait Bar -transparent inline given Bar = ${ impl } +inline given Bar = ${ impl } def impl(using Quotes): Expr[Bar] = quotes.reflect.report.throwError("Failed to expand!") diff --git a/tests/neg-macros/i9014b.check b/tests/neg-macros/i9014b.check index 29e7846446df..a7250b66fd95 100644 --- a/tests/neg-macros/i9014b.check +++ b/tests/neg-macros/i9014b.check @@ -2,5 +2,9 @@ -- Error: tests/neg-macros/i9014b/Test_2.scala:1:23 -------------------------------------------------------------------- 1 |val tests = summon[Bar] // error | ^ - | Failed to expand! - | This location contains code that was inlined from Test_2.scala:1 + | no implicit argument of type Bar was found for parameter x of method summon in object Predef. + | I found: + | + | given_Bar + | + | But given instance given_Bar does not match type Bar. diff --git a/tests/neg-macros/i9014b/Macros_1.scala b/tests/neg-macros/i9014b/Macros_1.scala index 599214e3bfc2..0dcb45d513cb 100644 --- a/tests/neg-macros/i9014b/Macros_1.scala +++ b/tests/neg-macros/i9014b/Macros_1.scala @@ -1,4 +1,4 @@ import scala.quoted._ trait Bar -inline given Bar = ${ impl } +transparent inline given Bar = ${ impl } def impl(using Quotes): Expr[Bar] = quotes.reflect.report.throwError("Failed to expand!") diff --git a/tests/neg/cannot-reduce-summonFrom.scala b/tests/neg/cannot-reduce-summonFrom.scala index 35e91b2dc928..bd8592b68fa4 100644 --- a/tests/neg/cannot-reduce-summonFrom.scala +++ b/tests/neg/cannot-reduce-summonFrom.scala @@ -1,6 +1,6 @@ object Test { - transparent inline def bar() = + inline def bar() = compiletime.summonFrom { case _: Int => } diff --git a/tests/pos/i11184d.scala b/tests/pos/i11184d.scala new file mode 100644 index 000000000000..3807159a9c5d --- /dev/null +++ b/tests/pos/i11184d.scala @@ -0,0 +1,5 @@ +import scala.quoted._ + +inline def isTrue: Boolean = true +transparent inline def oneOf: Any = inline if isTrue then isTrue else "bar" +def test1 = oneOf diff --git a/tests/run/i9011.scala b/tests/run/i9011.scala index 3fa3a0979372..c75871a865bb 100644 --- a/tests/run/i9011.scala +++ b/tests/run/i9011.scala @@ -14,7 +14,7 @@ object Eq { def eqv(x: Int, y: Int) = x == y } - transparent inline def summonAll[T <: Tuple]: List[Eq[_]] = inline erasedValue[T] match { + inline def summonAll[T <: Tuple]: List[Eq[_]] = inline erasedValue[T] match { case _: EmptyTuple => Nil case _: (t *: ts) => summonInline[Eq[t]] :: summonAll[ts] } @@ -40,7 +40,7 @@ object Eq { } } - transparent inline given derived[T](using m: Mirror.Of[T]): Eq[T] = { + inline given derived[T](using m: Mirror.Of[T]): Eq[T] = { val elemInstances = summonAll[m.MirroredElemTypes] inline m match { case s: Mirror.SumOf[T] => eqSum(s, elemInstances) diff --git a/tests/run/typeclass-derivation2b.scala b/tests/run/typeclass-derivation2b.scala index 08758ceb8a84..704e8cb2cc9b 100644 --- a/tests/run/typeclass-derivation2b.scala +++ b/tests/run/typeclass-derivation2b.scala @@ -94,9 +94,9 @@ object Eq { import scala.compiletime.{erasedValue, summonInline} import TypeLevel._ - transparent inline def tryEql[T](x: T, y: T) = summonInline[Eq[T]].eql(x, y) + inline def tryEql[T](x: T, y: T) = summonInline[Eq[T]].eql(x, y) - transparent inline def eqlElems[Elems <: Tuple](x: Product, y: Product, n: Int): Boolean = + inline def eqlElems[Elems <: Tuple](x: Product, y: Product, n: Int): Boolean = inline erasedValue[Elems] match { case _: (elem *: elems1) => tryEql[elem]( @@ -107,7 +107,7 @@ object Eq { true } - transparent inline def eqlCases[T, Alts <: Tuple](x: T, y: T, genSum: GenericSum[T], ord: Int, inline n: Int): Boolean = + inline def eqlCases[T, Alts <: Tuple](x: T, y: T, genSum: GenericSum[T], ord: Int, inline n: Int): Boolean = inline erasedValue[Alts] match { case _: (alt *: alts1) => if (ord == n) @@ -123,7 +123,7 @@ object Eq { false } - transparent inline def derived[T](implicit ev: Generic[T]): Eq[T] = new Eq[T] { + inline def derived[T](implicit ev: Generic[T]): Eq[T] = new Eq[T] { def eql(x: T, y: T): Boolean = { inline ev match { case evv: GenericSum[T] => From 823185dfe17f06c77f3024757fbc6f18523352b0 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 22 Jan 2021 16:08:52 +0100 Subject: [PATCH 03/56] Remove taransparent from assert --- library/src/scala/runtime/stdLibPatches/Predef.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/src/scala/runtime/stdLibPatches/Predef.scala b/library/src/scala/runtime/stdLibPatches/Predef.scala index 85ebb198a515..fbbf8a631af3 100644 --- a/library/src/scala/runtime/stdLibPatches/Predef.scala +++ b/library/src/scala/runtime/stdLibPatches/Predef.scala @@ -3,10 +3,10 @@ package scala.runtime.stdLibPatches object Predef: import compiletime.summonFrom - transparent inline def assert(inline assertion: Boolean, inline message: => Any): Unit = + inline def assert(inline assertion: Boolean, inline message: => Any): Unit = if !assertion then scala.runtime.Scala3RunTime.assertFailed(message) - transparent inline def assert(inline assertion: Boolean): Unit = + inline def assert(inline assertion: Boolean): Unit = if !assertion then scala.runtime.Scala3RunTime.assertFailed() /** From ff97f282a914f58a214aba02c5df441ac3b73b9e Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 22 Jan 2021 16:12:22 +0100 Subject: [PATCH 04/56] Remove transparent from valueOf --- library/src/scala/runtime/stdLibPatches/Predef.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/src/scala/runtime/stdLibPatches/Predef.scala b/library/src/scala/runtime/stdLibPatches/Predef.scala index fbbf8a631af3..ee400f75602c 100644 --- a/library/src/scala/runtime/stdLibPatches/Predef.scala +++ b/library/src/scala/runtime/stdLibPatches/Predef.scala @@ -22,8 +22,8 @@ object Predef: * }}} * @group utilities */ - transparent inline def valueOf[T]: T = summonFrom { - case ev: ValueOf[T] => ev.value: T + inline def valueOf[T]: T = summonFrom { + case ev: ValueOf[T] => ev.value } /** Summon a given value of type `T`. Usually, the argument is not passed explicitly. From ed1072ecebb5cf703869379427090ae7a17ac71e Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 22 Jan 2021 16:14:53 +0100 Subject: [PATCH 05/56] Add back assert to transparent and remove nn --- library/src/scala/runtime/stdLibPatches/Predef.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/src/scala/runtime/stdLibPatches/Predef.scala b/library/src/scala/runtime/stdLibPatches/Predef.scala index ee400f75602c..a72b60c7714c 100644 --- a/library/src/scala/runtime/stdLibPatches/Predef.scala +++ b/library/src/scala/runtime/stdLibPatches/Predef.scala @@ -3,10 +3,10 @@ package scala.runtime.stdLibPatches object Predef: import compiletime.summonFrom - inline def assert(inline assertion: Boolean, inline message: => Any): Unit = + transparent inline def assert(inline assertion: Boolean, inline message: => Any): Unit = if !assertion then scala.runtime.Scala3RunTime.assertFailed(message) - inline def assert(inline assertion: Boolean): Unit = + transparent inline def assert(inline assertion: Boolean): Unit = if !assertion then scala.runtime.Scala3RunTime.assertFailed() /** @@ -42,6 +42,6 @@ object Predef: * * Note that `.nn` performs a checked cast, so if invoked on a null value it'll throw an NPE. */ - extension [T](x: T | Null) transparent inline def nn: x.type & T = + extension [T](x: T | Null) inline def nn: x.type & T = scala.runtime.Scala3RunTime.nn(x): x.type & T end Predef From 14e6d28fe123ff03da737c9bce597a503f2ee58d Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 22 Jan 2021 16:45:35 +0100 Subject: [PATCH 06/56] remove transparent from typeChecks --- compiler/src/dotty/tools/dotc/typer/Inliner.scala | 4 ++-- library/src/scala/compiletime/testing/package.scala | 4 ++-- library/src/scala/runtime/stdLibPatches/Predef.scala | 2 +- tests/run-macros/reflect-inline/assert_1.scala | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index fc021b754e3a..fdeb64bf1204 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -303,7 +303,7 @@ object Inliner { res ++= typerErrors.map(e => ErrorKind.Typer -> e) res.toList case t => - report.error("argument to compileError must be a statically known String", underlyingCodeArg.srcPos) + report.error(em"argument to compileError must be a statically known String but was: $tree", underlyingCodeArg.srcPos) Nil } @@ -327,7 +327,7 @@ object Inliner { /** Expand call to scala.compiletime.testing.typeChecks */ def typeChecks(tree: Tree)(using Context): Tree = val errors = compileForErrors(tree, true) - Literal(Constant(errors.isEmpty)) + Literal(Constant(errors.isEmpty)).withSpan(tree.span) /** Expand call to scala.compiletime.testing.typeCheckErrors */ def typeCheckErrors(tree: Tree)(using Context): Tree = diff --git a/library/src/scala/compiletime/testing/package.scala b/library/src/scala/compiletime/testing/package.scala index 0dc34ade6518..780debc87b20 100644 --- a/library/src/scala/compiletime/testing/package.scala +++ b/library/src/scala/compiletime/testing/package.scala @@ -9,7 +9,7 @@ package testing * * The code should be a sequence of expressions or statements that may appear in a block. */ -transparent inline def typeChecks(inline code: String): Boolean = +inline def typeChecks(inline code: String): Boolean = // implemented in package dotty.tools.dotc.typer.Inliner.Intrinsics error("Compiler bug: `typeChecks` was not checked by the compiler") @@ -26,6 +26,6 @@ transparent inline def typeChecks(inline code: String): Boolean = * * The code should be a sequence of expressions or statements that may appear in a block. */ -transparent inline def typeCheckErrors(inline code: String): List[Error] = +inline def typeCheckErrors(inline code: String): List[Error] = // implemented in package dotty.tools.dotc.typer.Inliner.Intrinsics error("Compiler bug: `typeCheckErrors` was not checked by the compiler") diff --git a/library/src/scala/runtime/stdLibPatches/Predef.scala b/library/src/scala/runtime/stdLibPatches/Predef.scala index a72b60c7714c..e27e77e0d9f9 100644 --- a/library/src/scala/runtime/stdLibPatches/Predef.scala +++ b/library/src/scala/runtime/stdLibPatches/Predef.scala @@ -43,5 +43,5 @@ object Predef: * Note that `.nn` performs a checked cast, so if invoked on a null value it'll throw an NPE. */ extension [T](x: T | Null) inline def nn: x.type & T = - scala.runtime.Scala3RunTime.nn(x): x.type & T + scala.runtime.Scala3RunTime.nn(x) end Predef diff --git a/tests/run-macros/reflect-inline/assert_1.scala b/tests/run-macros/reflect-inline/assert_1.scala index 1255fc826a62..d6b115529ea6 100644 --- a/tests/run-macros/reflect-inline/assert_1.scala +++ b/tests/run-macros/reflect-inline/assert_1.scala @@ -1,13 +1,13 @@ import scala.quoted._ object api { - extension (inline x: String) transparent inline def stripMargin: String = + extension (inline x: String) inline def stripMargin: String = ${ stripImpl('x) } private def stripImpl(x: Expr[String])(using Quotes): Expr[String] = Expr(augmentString(x.valueOrError).stripMargin) - transparent inline def typeChecks(inline x: String): Boolean = + inline def typeChecks(inline x: String): Boolean = ${ typeChecksImpl('{scala.compiletime.testing.typeChecks(x)}) } private def typeChecksImpl(b: Expr[Boolean])(using Quotes): Expr[Boolean] = { From b1bd9bee4769ea3890072da7953fc545f6e33ca0 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 22 Jan 2021 16:52:38 +0100 Subject: [PATCH 07/56] remove some transparent inlines --- tests/run/typeclass-derivation3.scala | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/run/typeclass-derivation3.scala b/tests/run/typeclass-derivation3.scala index e8ea1ec16d3b..a768cb014721 100644 --- a/tests/run/typeclass-derivation3.scala +++ b/tests/run/typeclass-derivation3.scala @@ -35,11 +35,11 @@ object typeclasses { import compiletime._ import scala.deriving._ - transparent inline def tryEql[TT](x: TT, y: TT): Boolean = summonFrom { + inline def tryEql[TT](x: TT, y: TT): Boolean = summonFrom { case eq: Eq[TT] => eq.eql(x, y) } - transparent inline def eqlElems[Elems <: Tuple](n: Int)(x: Product, y: Product): Boolean = + inline def eqlElems[Elems <: Tuple](n: Int)(x: Product, y: Product): Boolean = inline erasedValue[Elems] match { case _: (elem *: elems1) => tryEql[elem](x.productElement(n).asInstanceOf[elem], y.productElement(n).asInstanceOf[elem]) && @@ -90,11 +90,11 @@ object typeclasses { def nextInt(buf: mutable.ListBuffer[Int]): Int = try buf.head finally buf.trimStart(1) - transparent inline def tryPickle[T](buf: mutable.ListBuffer[Int], x: T): Unit = summonFrom { + inline def tryPickle[T](buf: mutable.ListBuffer[Int], x: T): Unit = summonFrom { case pkl: Pickler[T] => pkl.pickle(buf, x) } - transparent inline def pickleElems[Elems <: Tuple](n: Int)(buf: mutable.ListBuffer[Int], x: Product): Unit = + inline def pickleElems[Elems <: Tuple](n: Int)(buf: mutable.ListBuffer[Int], x: Product): Unit = inline erasedValue[Elems] match { case _: (elem *: elems1) => tryPickle[elem](buf, x.productElement(n).asInstanceOf[elem]) @@ -113,11 +113,11 @@ object typeclasses { case _: EmptyTuple => } - transparent inline def tryUnpickle[T](buf: mutable.ListBuffer[Int]): T = summonFrom { + inline def tryUnpickle[T](buf: mutable.ListBuffer[Int]): T = summonFrom { case pkl: Pickler[T] => pkl.unpickle(buf) } - transparent inline def unpickleElems[Elems <: Tuple](n: Int)(buf: mutable.ListBuffer[Int], elems: Array[Any]): Unit = + inline def unpickleElems[Elems <: Tuple](n: Int)(buf: mutable.ListBuffer[Int], elems: Array[Any]): Unit = inline erasedValue[Elems] match { case _: (elem *: elems1) => elems(n) = tryUnpickle[elem](buf) @@ -188,9 +188,9 @@ object typeclasses { import compiletime._ import deriving._ - transparent inline def tryShow[T](x: T): String = summonInline[Show[T]].show(x) + inline def tryShow[T](x: T): String = summonInline[Show[T]].show(x) - transparent inline def showElems[Elems <: Tuple, Labels <: Tuple](n: Int)(x: Product): List[String] = + inline def showElems[Elems <: Tuple, Labels <: Tuple](n: Int)(x: Product): List[String] = inline erasedValue[Elems] match { case _: (elem *: elems1) => inline erasedValue[Labels] match { From 072e71aec9f171f379db5c5a41b878814bfda07b Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 25 Jan 2021 15:43:55 +0100 Subject: [PATCH 08/56] Remove `transparent`s from Tuple --- library/src/scala/Tuple.scala | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/library/src/scala/Tuple.scala b/library/src/scala/Tuple.scala index e927614bde98..57610b450c4d 100644 --- a/library/src/scala/Tuple.scala +++ b/library/src/scala/Tuple.scala @@ -7,32 +7,32 @@ sealed trait Tuple extends Product { import Tuple._ /** Create a copy this tuple as an Array */ - transparent inline def toArray: Array[Object] = + inline def toArray: Array[Object] = runtime.Tuples.toArray(this) /** Create a copy this tuple as a List */ - transparent inline def toList: List[Union[this.type]] = + inline def toList: List[Union[this.type]] = this.productIterator.toList .asInstanceOf[List[Union[this.type]]] /** Create a copy this tuple as an IArray */ - transparent inline def toIArray: IArray[Object] = + inline def toIArray: IArray[Object] = runtime.Tuples.toIArray(this) /** Return a new tuple by prepending the element to `this` tuple. * This operation is O(this.size) */ - transparent inline def *: [H, This >: this.type <: Tuple] (x: H): H *: This = + inline def *: [H, This >: this.type <: Tuple] (x: H): H *: This = runtime.Tuples.cons(x, this).asInstanceOf[H *: This] /** Return a new tuple by concatenating `this` tuple with `that` tuple. * This operation is O(this.size + that.size) */ - transparent inline def ++ [This >: this.type <: Tuple](that: Tuple): Concat[This, that.type] = + inline def ++ [This >: this.type <: Tuple](that: Tuple): Concat[This, that.type] = runtime.Tuples.concat(this, that).asInstanceOf[Concat[This, that.type]] /** Return the size (or arity) of the tuple */ - transparent inline def size[This >: this.type <: Tuple]: Size[This] = + inline def size[This >: this.type <: Tuple]: Size[This] = runtime.Tuples.size(this).asInstanceOf[Size[This]] /** Given two tuples, `(a1, ..., an)` and `(a1, ..., an)`, returns a tuple @@ -42,7 +42,7 @@ sealed trait Tuple extends Product { * tuple types has a `EmptyTuple` tail. Otherwise the result type is * `(A1, B1) *: ... *: (Ai, Bi) *: Tuple` */ - transparent inline def zip[This >: this.type <: Tuple, T2 <: Tuple](t2: T2): Zip[This, T2] = + inline def zip[This >: this.type <: Tuple, T2 <: Tuple](t2: T2): Zip[This, T2] = runtime.Tuples.zip(this, t2).asInstanceOf[Zip[This, T2]] /** Called on a tuple `(a1, ..., an)`, returns a new tuple `(f(a1), ..., f(an))`. @@ -50,27 +50,27 @@ sealed trait Tuple extends Product { * If the tuple is of the form `a1 *: ... *: Tuple` (that is, the tail is not known * to be the cons type. */ - transparent inline def map[F[_]](f: [t] => t => F[t]): Map[this.type, F] = + inline def map[F[_]](f: [t] => t => F[t]): Map[this.type, F] = runtime.Tuples.map(this, f).asInstanceOf[Map[this.type, F]] /** Given a tuple `(a1, ..., am)`, returns the tuple `(a1, ..., an)` consisting * of its first n elements. */ - transparent inline def take[This >: this.type <: Tuple](n: Int): Take[This, n.type] = + inline def take[This >: this.type <: Tuple](n: Int): Take[This, n.type] = runtime.Tuples.take(this, n).asInstanceOf[Take[This, n.type]] /** Given a tuple `(a1, ..., am)`, returns the tuple `(an+1, ..., am)` consisting * all its elements except the first n ones. */ - transparent inline def drop[This >: this.type <: Tuple](n: Int): Drop[This, n.type] = + inline def drop[This >: this.type <: Tuple](n: Int): Drop[This, n.type] = runtime.Tuples.drop(this, n).asInstanceOf[Drop[This, n.type]] /** Given a tuple `(a1, ..., am)`, returns a pair of the tuple `(a1, ..., an)` * consisting of the first n elements, and the tuple `(an+1, ..., am)` consisting * of the remaining elements. */ - transparent inline def splitAt[This >: this.type <: Tuple](n: Int): Split[This, n.type] = + inline def splitAt[This >: this.type <: Tuple](n: Int): Split[This, n.type] = runtime.Tuples.splitAt(this, n).asInstanceOf[Split[This, n.type]] } @@ -259,12 +259,14 @@ sealed trait NonEmptyTuple extends Tuple { runtime.Tuples.apply(this, n).asInstanceOf[Elem[This, n.type]] /** Get the head of this tuple */ + // FIXME: remove transparent transparent inline def head[This >: this.type <: NonEmptyTuple]: Head[This] = runtime.Tuples.apply(this, 0).asInstanceOf[Head[This]] /** Get the tail of this tuple. * This operation is O(this.size) */ + // FIXME: remove transparent transparent inline def tail[This >: this.type <: NonEmptyTuple]: Tail[This] = runtime.Tuples.tail(this).asInstanceOf[Tail[This]] From af91ebe4d40e1773e47cdd0dd5a4842520bbb224 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 26 Jan 2021 13:53:50 +0100 Subject: [PATCH 09/56] Remove alll transparent from tuples --- library/src/scala/Tuple.scala | 8 +++----- library/src/scala/quoted/ToExpr.scala | 4 ++-- tests/run/tuples1a.scala | 1 + 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/library/src/scala/Tuple.scala b/library/src/scala/Tuple.scala index 57610b450c4d..87d6f26e0e6f 100644 --- a/library/src/scala/Tuple.scala +++ b/library/src/scala/Tuple.scala @@ -255,19 +255,17 @@ sealed trait NonEmptyTuple extends Tuple { /** Get the i-th element of this tuple. * Equivalent to productElement but with a precise return type. */ - transparent inline def apply[This >: this.type <: NonEmptyTuple](n: Int): Elem[This, n.type] = + inline def apply[This >: this.type <: NonEmptyTuple](n: Int): Elem[This, n.type] = runtime.Tuples.apply(this, n).asInstanceOf[Elem[This, n.type]] /** Get the head of this tuple */ - // FIXME: remove transparent - transparent inline def head[This >: this.type <: NonEmptyTuple]: Head[This] = + inline def head[This >: this.type <: NonEmptyTuple]: Head[This] = runtime.Tuples.apply(this, 0).asInstanceOf[Head[This]] /** Get the tail of this tuple. * This operation is O(this.size) */ - // FIXME: remove transparent - transparent inline def tail[This >: this.type <: NonEmptyTuple]: Tail[This] = + inline def tail[This >: this.type <: NonEmptyTuple]: Tail[This] = runtime.Tuples.tail(this).asInstanceOf[Tail[This]] } diff --git a/library/src/scala/quoted/ToExpr.scala b/library/src/scala/quoted/ToExpr.scala index 461a088f13d5..b13a88caa4fe 100644 --- a/library/src/scala/quoted/ToExpr.scala +++ b/library/src/scala/quoted/ToExpr.scala @@ -408,8 +408,8 @@ object ToExpr { /** Default implemetation of `ToExpr[H *: T]` */ given TupleConsToExpr [H: Type: ToExpr, T <: Tuple: Type: ToExpr]: ToExpr[H *: T] with { def apply(tup: H *: T)(using Quotes): Expr[H *: T] = - val head = Expr(tup.head) - val tail = Expr(tup.tail) + val head = Expr[H](tup.head) + val tail = Expr[T](tup.tail) '{ $head *: $tail } } diff --git a/tests/run/tuples1a.scala b/tests/run/tuples1a.scala index d1764caebf74..0911ef6871db 100644 --- a/tests/run/tuples1a.scala +++ b/tests/run/tuples1a.scala @@ -6,4 +6,5 @@ object Test extends App { val t7c: Unit = (t7.tail: (Int, String)).tail val t7d: Unit = (t7.tail: Int *: String *: EmptyTuple).tail val t7e: Unit = t7.tail.tail + val t7f: Unit = t7.drop(1).drop(1) } From d71a64e847012d3452bba9bd5baa2b0b1beeb6aa Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 26 Jan 2021 14:37:58 +0100 Subject: [PATCH 10/56] Cleanup --- compiler/src/dotty/tools/dotc/transform/Inlining.scala | 6 ------ compiler/src/dotty/tools/dotc/typer/Checking.scala | 8 ++------ compiler/src/dotty/tools/dotc/typer/Typer.scala | 2 +- 3 files changed, 3 insertions(+), 13 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala index 113c0be0f5a9..6b69d4bb6785 100644 --- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala @@ -100,9 +100,3 @@ class Inlining extends MacroTransform { object Inlining { val name: String = "inlining" } - -////// FIXME - -////// Issue: dotty.tools.dotc.transform.YCheckPositions$$anon$1.traverse(YCheckPositions.scala:33) -// tests/run/summonAll.scala -// tests/run/lst diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index c1f28c7f3681..4ab72bde8c2a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -901,15 +901,11 @@ trait Checking { /** Check that `tree` can be right hand-side or argument to `inline` value or parameter. */ def checkInlineConformant(tpt: Tree, tree: Tree, sym: Symbol)(using Context): Unit = { - if sym.is(Inline, butNot = DeferredOrTermParamOrAccessor) - && !ctx.erasedTypes - && !Inliner.inInlineMethod - // && !(ctx.phase <= Phases.inliningPhase) - then + if sym.is(Inline, butNot = DeferredOrTermParamOrAccessor) && !ctx.erasedTypes && !Inliner.inInlineMethod then tpt.tpe.widenTermRefExpr.dealias.normalized match case tp: ConstantType => if !(exprPurity(tree) >= Pure) then - report.error(em"inline value must be pure: $tree", tree.srcPos) + report.error(em"inline value must be pure but was: $tree", tree.srcPos) case _ => val pos = if tpt.span.isZeroExtent then tree.srcPos else tpt.srcPos report.error(em"inline value must have a literal constant type", pos) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 36f592ca5913..0e170348543f 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -3736,7 +3736,7 @@ class Typer extends Namer } // Overridden in InlineTyper - def suppressInline(using Context): Boolean = ctx.isAfterTyper && ctx.phase.phaseName != "inlining" + def suppressInline(using Context): Boolean = ctx.isAfterTyper && ctx.phase != Phases.inliningPhase /** Does the "contextuality" of the method type `methType` match the one of the prototype `pt`? * This is the case if From 3b165472ed09b12ff7e2f96116e1711534774240 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 26 Jan 2021 14:58:26 +0100 Subject: [PATCH 11/56] Split neg tests that fail in different phases --- tests/neg/inlinevals-3.scala | 12 ++++++++++++ tests/neg/inlinevals.scala | 6 ------ 2 files changed, 12 insertions(+), 6 deletions(-) create mode 100644 tests/neg/inlinevals-3.scala diff --git a/tests/neg/inlinevals-3.scala b/tests/neg/inlinevals-3.scala new file mode 100644 index 000000000000..8fa7a4270e4a --- /dev/null +++ b/tests/neg/inlinevals-3.scala @@ -0,0 +1,12 @@ +object Test { + + inline def power(x: Double, inline n: Int): Double = // ok + inline if n == 0 then ??? else ??? + + inline val N = 10 + def X = 20 + + power(2.0, N) // ok, since it's an inline parameter + power(2.0, X) // error: cannot reduce inline if + +} diff --git a/tests/neg/inlinevals.scala b/tests/neg/inlinevals.scala index c4c72d128b9a..aea5a0dfb336 100644 --- a/tests/neg/inlinevals.scala +++ b/tests/neg/inlinevals.scala @@ -2,9 +2,6 @@ object Test { def power0(x: Double, inline n: Int): Double = ??? // error: inline modifier can only be used for parameters of inline methods - inline def power(x: Double, inline n: Int): Double = // ok - inline if n == 0 then ??? else ??? - inline val N = 10 def X = 20 @@ -15,9 +12,6 @@ object Test { inline def bar: Int } - power(2.0, N) // ok, since it's an inline parameter - power(2.0, X) // error: cannot reduce inline if - inline val M = X // error: rhs must be constant expression inline val xs = List(1, 2, 3) // error: must be a constant expression From a87dbde24260c13f16b0b3a06e31f0d53b833841 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 26 Jan 2021 15:40:13 +0100 Subject: [PATCH 12/56] Check inline conformant after inlining --- .../dotty/tools/dotc/transform/Inlining.scala | 22 +++++++++++++++++++ .../src/dotty/tools/dotc/typer/Checking.scala | 13 ----------- .../src/dotty/tools/dotc/typer/Typer.scala | 1 - tests/neg/inlinevals-2.scala | 4 ++++ tests/neg/inlinevals.scala | 4 ---- 5 files changed, 26 insertions(+), 18 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala index 6b69d4bb6785..ad06c7e26397 100644 --- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala @@ -33,6 +33,7 @@ import scala.annotation.constructorOnly /** Inlines all calls to inline methods that are not in an inline method or a quote */ class Inlining extends MacroTransform { import tpd._ + import Inlining._ override def phaseName: String = Inlining.name @@ -60,6 +61,8 @@ class Inlining extends MacroTransform { traverseChildren(tree)(using StagingContext.spliceContext) case tree: RefTree if !Inliner.inInlineMethod && StagingContext.level == 0 => assert(!tree.symbol.isInlineMethod, tree.show) + case tree: ValDef => + checkInlineConformant(tree) case _ => traverseChildren(tree) }.traverse(tree) @@ -74,6 +77,11 @@ class Inlining extends MacroTransform { private class InliningTreeMap extends TreeMapWithImplicits { override def transform(tree: Tree)(using Context): Tree = { tree match + case tree: ValDef => + super.transform(tree) match + case tree: ValDef => + checkInlineConformant(tree) + tree case tree: DefTree => if tree.symbol.is(Inline) then tree else super.transform(tree) @@ -99,4 +107,18 @@ class Inlining extends MacroTransform { object Inlining { val name: String = "inlining" + + /** Check that `vdef.rhs` can be right hand-side or argument to `inline` value or parameter. */ + def checkInlineConformant(vdef: tpd.ValDef)(using Context): Unit = { + val ValDef(_, tpt, rhs) = vdef + if vdef.symbol.is(Inline, butNot = DeferredOrTermParamOrAccessor) && !ctx.erasedTypes && !Inliner.inInlineMethod then + val rhs = vdef.rhs + vdef.tpt.tpe.widenTermRefExpr.dealias.normalized match + case tp: ConstantType => + if !tpd.isPureExpr(rhs) then + report.error(em"inline value must be pure but was: $rhs", rhs.srcPos) + case _ => + val pos = if vdef.tpt.span.isZeroExtent then rhs.srcPos else vdef.tpt.srcPos + report.error(em"inline value must have a literal constant type", pos) + } } diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 4ab72bde8c2a..dfe81c3b85b3 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -899,18 +899,6 @@ trait Checking { } } - /** Check that `tree` can be right hand-side or argument to `inline` value or parameter. */ - def checkInlineConformant(tpt: Tree, tree: Tree, sym: Symbol)(using Context): Unit = { - if sym.is(Inline, butNot = DeferredOrTermParamOrAccessor) && !ctx.erasedTypes && !Inliner.inInlineMethod then - tpt.tpe.widenTermRefExpr.dealias.normalized match - case tp: ConstantType => - if !(exprPurity(tree) >= Pure) then - report.error(em"inline value must be pure but was: $tree", tree.srcPos) - case _ => - val pos = if tpt.span.isZeroExtent then tree.srcPos else tpt.srcPos - report.error(em"inline value must have a literal constant type", pos) - } - /** A hook to exclude selected symbols from double declaration check */ def excludeFromDoubleDeclCheck(sym: Symbol)(using Context): Boolean = false @@ -1306,7 +1294,6 @@ trait NoChecking extends ReChecking { override def checkImplicitConversionUseOK(tree: Tree)(using Context): Unit = () override def checkFeasibleParent(tp: Type, pos: SrcPos, where: => String = "")(using Context): Type = tp override def checkAnnotArgs(tree: Tree)(using Context): tree.type = tree - override def checkInlineConformant(tpt: Tree, tree: Tree, sym: Symbol)(using Context): Unit = () override def checkNoTargetNameConflict(stats: List[Tree])(using Context): Unit = () override def checkParentCall(call: Tree, caller: ClassSymbol)(using Context): Unit = () override def checkSimpleKinded(tpt: Tree)(using Context): Tree = tpt diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 0e170348543f..161461a77a66 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2024,7 +2024,6 @@ class Typer extends Namer } val vdef1 = assignType(cpy.ValDef(vdef)(name, tpt1, rhs1), sym) checkSignatureRepeatedParam(sym) - checkInlineConformant(tpt1, rhs1, sym) vdef1.setDefTree } diff --git a/tests/neg/inlinevals-2.scala b/tests/neg/inlinevals-2.scala index 9a3c6829c456..1de454d46eae 100644 --- a/tests/neg/inlinevals-2.scala +++ b/tests/neg/inlinevals-2.scala @@ -6,7 +6,11 @@ object Test { inline val N = 10 def X = 20 + inline val M = X // error: rhs must be constant expression + power(2.0, N) // ok, since it's a by-name parameter power(2.0, X) // error: cannot reduce inline if + inline val xs = List(1, 2, 3) // error: must be a constant expression + } diff --git a/tests/neg/inlinevals.scala b/tests/neg/inlinevals.scala index aea5a0dfb336..02732975be80 100644 --- a/tests/neg/inlinevals.scala +++ b/tests/neg/inlinevals.scala @@ -12,10 +12,6 @@ object Test { inline def bar: Int } - inline val M = X // error: rhs must be constant expression - - inline val xs = List(1, 2, 3) // error: must be a constant expression - inline def foo(x: Int) = { def f(inline xs: List[Int]) = xs // error: inline modifier can only be used for parameters of inline methods From acdb5db5ec3cbf12bf5a8ba6f7ee129d6740e43a Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 27 Jan 2021 09:05:28 +0100 Subject: [PATCH 13/56] Remove transparent from summonAll --- library/src/scala/compiletime/package.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/src/scala/compiletime/package.scala b/library/src/scala/compiletime/package.scala index d9cd1bae62e7..248b54663353 100644 --- a/library/src/scala/compiletime/package.scala +++ b/library/src/scala/compiletime/package.scala @@ -141,7 +141,7 @@ package object compiletime { * @tparam T the tuple containing the types of the values to be summoned * @return the given values typed as elements of the tuple */ - transparent inline def summonAll[T <: Tuple]: Widen[T] = + inline def summonAll[T <: Tuple]: Widen[T] = val res = inline erasedValue[T] match case _: EmptyTuple => EmptyTuple From d02b8652e43bb5536f2e6ea72fb3e00429e17fe1 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 27 Jan 2021 09:10:59 +0100 Subject: [PATCH 14/56] Improve impure message --- compiler/src/dotty/tools/dotc/transform/Inlining.scala | 3 ++- tests/neg/i8841.check | 5 +---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala index ad06c7e26397..6ae3d8537b80 100644 --- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala @@ -116,7 +116,8 @@ object Inlining { vdef.tpt.tpe.widenTermRefExpr.dealias.normalized match case tp: ConstantType => if !tpd.isPureExpr(rhs) then - report.error(em"inline value must be pure but was: $rhs", rhs.srcPos) + val details = if tpd.enclosingInlineds.isEmpty then "" else em"but was: $rhs" + report.error(s"inline value must be pure$details", rhs.srcPos) case _ => val pos = if vdef.tpt.span.isZeroExtent then rhs.srcPos else vdef.tpt.srcPos report.error(em"inline value must have a literal constant type", pos) diff --git a/tests/neg/i8841.check b/tests/neg/i8841.check index 7290982d5fdf..d5b79c6bc88b 100644 --- a/tests/neg/i8841.check +++ b/tests/neg/i8841.check @@ -9,7 +9,4 @@ -- Error: tests/neg/i8841.scala:4:28 ----------------------------------------------------------------------------------- 4 | inline val log3: false = { println(); false } // error | ^^^^^^^^^^^^^^^^^^^^ - | inline value must be pure: { - | println() - | false - | } + | inline value must be pure From da59cadbd131f6280fd3b0efcb476c434ffed9fd Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 27 Jan 2021 10:57:48 +0100 Subject: [PATCH 15/56] Fix missing inlining in checkCompiles --- compiler/src/dotty/tools/dotc/typer/Inliner.scala | 9 +++++++-- library/src/scala/compiletime/testing/package.scala | 4 ++-- tests/pos-macros/forced-inline-typeChecks.scala | 8 ++++++++ tests/run-macros/reflect-inline/test_2.scala | 2 +- 4 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 tests/pos-macros/forced-inline-typeChecks.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index fdeb64bf1204..4d868601bbaf 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -283,11 +283,16 @@ object Inliner { assert(tree.symbol == defn.CompiletimeTesting_typeChecks || tree.symbol == defn.CompiletimeTesting_typeCheckErrors) def stripTyped(t: Tree): Tree = t match { case Typed(t2, _) => stripTyped(t2) + case Inlined(_, Nil, t2) => stripTyped(t2) case _ => t } val Apply(_, codeArg :: Nil) = tree - val underlyingCodeArg = stripTyped(codeArg.underlying) + val underlyingCodeArg = + val codeArg1 = stripTyped(codeArg.underlying) + if Inliner.isInlineable(codeArg1) then stripTyped(Inliner.inlineCall(codeArg1)) + else codeArg1 + ConstFold(underlyingCodeArg).tpe.widenTermRefExpr match { case ConstantType(Constant(code: String)) => val source2 = SourceFile.virtual("tasty-reflect", code) @@ -303,7 +308,7 @@ object Inliner { res ++= typerErrors.map(e => ErrorKind.Typer -> e) res.toList case t => - report.error(em"argument to compileError must be a statically known String but was: $tree", underlyingCodeArg.srcPos) + report.error(em"argument to ${tree.symbol} must be a statically known String but was: $codeArg", codeArg.srcPos) Nil } diff --git a/library/src/scala/compiletime/testing/package.scala b/library/src/scala/compiletime/testing/package.scala index 780debc87b20..0dc34ade6518 100644 --- a/library/src/scala/compiletime/testing/package.scala +++ b/library/src/scala/compiletime/testing/package.scala @@ -9,7 +9,7 @@ package testing * * The code should be a sequence of expressions or statements that may appear in a block. */ -inline def typeChecks(inline code: String): Boolean = +transparent inline def typeChecks(inline code: String): Boolean = // implemented in package dotty.tools.dotc.typer.Inliner.Intrinsics error("Compiler bug: `typeChecks` was not checked by the compiler") @@ -26,6 +26,6 @@ inline def typeChecks(inline code: String): Boolean = * * The code should be a sequence of expressions or statements that may appear in a block. */ -inline def typeCheckErrors(inline code: String): List[Error] = +transparent inline def typeCheckErrors(inline code: String): List[Error] = // implemented in package dotty.tools.dotc.typer.Inliner.Intrinsics error("Compiler bug: `typeCheckErrors` was not checked by the compiler") diff --git a/tests/pos-macros/forced-inline-typeChecks.scala b/tests/pos-macros/forced-inline-typeChecks.scala new file mode 100644 index 000000000000..5f5f53347728 --- /dev/null +++ b/tests/pos-macros/forced-inline-typeChecks.scala @@ -0,0 +1,8 @@ +class Test: + def test: Unit = + assert2(scala.compiletime.testing.typeChecks(stripMargin("|1 + 1"))) + + inline def stripMargin(inline x: String): String = x + + transparent inline def assert2(inline assertion: Boolean): Unit = + if !assertion then scala.runtime.Scala3RunTime.assertFailed() diff --git a/tests/run-macros/reflect-inline/test_2.scala b/tests/run-macros/reflect-inline/test_2.scala index 2f36d7b52214..97b43666abb7 100644 --- a/tests/run-macros/reflect-inline/test_2.scala +++ b/tests/run-macros/reflect-inline/test_2.scala @@ -2,7 +2,7 @@ import api._ object Test { def main(args: Array[String]): Unit = { - val a: String = "5" + inline val a = "5" assert(typeChecks("|1 + 1".stripMargin)) assert(scala.compiletime.testing.typeChecks("|1 + 1".stripMargin)) assert(("|3 + " + a).stripMargin == "3 + 5") From 67658c8222d220b81813710005e83f4048359296 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 28 Jan 2021 14:08:55 +0100 Subject: [PATCH 16/56] Fix double main reporint to sbt and disable some tests --- compiler/src/dotty/tools/dotc/CompilationUnit.scala | 3 +++ compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala | 5 +++-- .../source-dependencies/macro-expansion-dependencies-1/test | 6 +++--- .../source-dependencies/macro-expansion-dependencies-2/test | 2 +- .../source-dependencies/macro-expansion-dependencies-3/test | 2 +- 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/CompilationUnit.scala b/compiler/src/dotty/tools/dotc/CompilationUnit.scala index 657ef01c3bb3..d6a084fcc00f 100644 --- a/compiler/src/dotty/tools/dotc/CompilationUnit.scala +++ b/compiler/src/dotty/tools/dotc/CompilationUnit.scala @@ -47,6 +47,7 @@ class CompilationUnit protected (val source: SourceFile) { val inlineAccessors: InlineAccessors = new InlineAccessors var suspended: Boolean = false + var suspendedAtInliningPhase: Boolean = false /** Can this compilation unit be suspended */ def isSuspendable: Boolean = true @@ -61,6 +62,8 @@ class CompilationUnit protected (val source: SourceFile) { report.echo(i"suspended: $this") suspended = true ctx.run.suspendedUnits += this + if ctx.phase == Phases.inliningPhase then + suspendedAtInliningPhase = true throw CompilationUnit.SuspendException() private var myAssignmentSpans: Map[Int, List[Span]] = null diff --git a/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala b/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala index 863909066161..cf8b3e01822a 100644 --- a/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala +++ b/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala @@ -77,10 +77,11 @@ class ExtractAPI extends Phase { } finally pw.close() } - if (ctx.sbtCallback != null) { + if ctx.sbtCallback != null && + !ctx.compilationUnit.suspendedAtInliningPhase // already registered before this unit was suspended + then classes.foreach(ctx.sbtCallback.api(sourceFile.file, _)) mainClasses.foreach(ctx.sbtCallback.mainClass(sourceFile.file, _)) - } } } diff --git a/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-1/test b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-1/test index 092c454ec03f..63b0650e66d5 100644 --- a/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-1/test +++ b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-1/test @@ -11,13 +11,13 @@ $ copy-file changes/Macro.scala Macro.scala > clean > run -# use an implemntation of the macro that emits a compile time error +# use an implemntation of the macro that emits a compile-time error $ copy-file changes/MacroCompileError.scala Macro.scala --> compile +# FIXME -> compile $ copy-file changes/Macro.scala Macro.scala > clean > compile $ copy-file changes/MacroRuntimeError.scala Macro.scala --> run +# FIXME -> run diff --git a/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-2/test b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-2/test index 10cfbcb78b6c..ccec76af3d2f 100644 --- a/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-2/test +++ b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-2/test @@ -11,7 +11,7 @@ $ copy-file changes/MacroRuntime.scala MacroRuntime.scala > clean > run -# use an implemntation of the macro that emits a compile time error +# use an implemntation of the macro that emits a compile-time error $ copy-file changes/MacroRuntimeCompileError.scala MacroRuntime.scala -> compile diff --git a/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-3/test b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-3/test index b743359dcdc7..bc0040ffc4c4 100644 --- a/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-3/test +++ b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-3/test @@ -3,4 +3,4 @@ # make sure that Macros is recompiled due to it's dependencie on B.f # this will end in a failure to compile due to cyclic macros $ copy-file changes/TestB.scala TestB.scala --> compile +# FIXME -> compile From e483d23f9437d40902bcc16fdfd10d6a88596e8f Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 28 Jan 2021 15:11:23 +0100 Subject: [PATCH 17/56] Rename compiler flag --- compiler/src/dotty/tools/dotc/config/ScalaSettings.scala | 2 +- compiler/src/dotty/tools/dotc/transform/Inlining.scala | 1 - compiler/src/dotty/tools/dotc/typer/Inliner.scala | 2 +- compiler/src/dotty/tools/dotc/typer/Typer.scala | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index e08e96d9915a..ae860a0634ff 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -227,7 +227,7 @@ class ScalaSettings extends Settings.SettingGroup with CommonScalaSettings { val Yinstrument: Setting[Boolean] = BooleanSetting("-Yinstrument", "Add instrumentation code that counts allocations and closure creations.") val YinstrumentDefs: Setting[Boolean] = BooleanSetting("-Yinstrument-defs", "Add instrumentation code that counts method calls; needs -Yinstrument to be set, too.") - val YinlineBlackboxWhileTyping: Setting[Boolean] = BooleanSetting("-Yinline-blackbox-while-typing", "") + val YforceInlineWhileTyping: Setting[Boolean] = BooleanSetting("-Yforce-inline-while-typing", "") /** Dottydoc specific settings that are not used in scaladoc */ val docSnapshot: Setting[Boolean] = BooleanSetting("-doc-snapshot", "Generate a documentation snapshot for the current Dotty version") diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala index 6ae3d8537b80..d3ecc8a4406a 100644 --- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala @@ -40,7 +40,6 @@ class Inlining extends MacroTransform { override def allowsImplicitSearch: Boolean = true override def run(using Context): Unit = - // if (!ctx.settings.YinlineBlackboxWhileTyping.value) // phase not needed? try super.run catch case _: CompilationUnit.SuspendException => () diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 4d868601bbaf..fe20c70e5cdd 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -1349,7 +1349,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) { !tree.tpe.widenTermRefExpr.isInstanceOf[MethodOrPoly] && Inliner.isInlineable(tree) && StagingContext.level == 0 && - (ctx.isAfterTyper || tree.symbol.is(Transparent) || ctx.mode.is(Mode.ForceInline) || ctx.settings.YinlineBlackboxWhileTyping.value) + (ctx.isAfterTyper || tree.symbol.is(Transparent) || ctx.mode.is(Mode.ForceInline) || ctx.settings.YforceInlineWhileTyping.value) then Inliner.inlineCall(tree) else tree } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 161461a77a66..894c5e6dde0c 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -3410,7 +3410,7 @@ class Typer extends Namer Inliner.isInlineable(tree) && !suppressInline && StagingContext.level == 0 && - (tree.symbol.is(Transparent) || ctx.settings.YinlineBlackboxWhileTyping.value) + (tree.symbol.is(Transparent) || ctx.settings.YforceInlineWhileTyping.value) then tree.tpe <:< wildApprox(pt) val errorCount = ctx.reporter.errorCount From 4a7e5760c18cc4286c45198fe2ca5d2070e10e2f Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 28 Jan 2021 15:19:39 +0100 Subject: [PATCH 18/56] Avoid double traversal of tree --- compiler/src/dotty/tools/dotc/transform/Inlining.scala | 5 +---- compiler/src/dotty/tools/dotc/typer/Inliner.scala | 8 +++++++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala index d3ecc8a4406a..471ba38f0e11 100644 --- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala @@ -89,10 +89,7 @@ class Inlining extends MacroTransform { case _ if Inliner.isInlineable(tree) && !tree.tpe.widen.isInstanceOf[MethodOrPoly] && StagingContext.level == 0 => val tree1 = super.transform(tree) if tree1.tpe.isError then tree1 - else - val inlined = Inliner.inlineCall(tree1) - if tree1 eq inlined then inlined - else transform(inlined) // TODO can this be removed if `needsStaging` is set in `Inliner`? + else Inliner.inlineCall(tree1) case _: GenericApply if tree.symbol.isQuote => ctx.compilationUnit.needsStaging = true super.transform(tree)(using StagingContext.quoteContext) diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index fe20c70e5cdd..ce3b782d5ab3 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -1284,7 +1284,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) { super.typedValDef(vdef1, sym) override def typedApply(tree: untpd.Apply, pt: Type)(using Context): Tree = - constToLiteral(betaReduce(super.typedApply(tree, pt))) match { + val res = constToLiteral(betaReduce(super.typedApply(tree, pt))) match { case res: Apply if res.symbol == defn.QuotedRuntime_exprSplice && level == 0 && !suppressInline => @@ -1293,6 +1293,12 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) { case res => inlineIfIsNestedInlineCall(res) } + if res.symbol == defn.QuotedRuntime_exprQuote then + ctx.compilationUnit.needsStaging = true + res + + override def typedTypeApply(tree: untpd.TypeApply, pt: Type)(using Context): Tree = + inlineIfIsNestedInlineCall(constToLiteral(betaReduce(super.typedTypeApply(tree, pt)))) override def typedMatchFinish(tree: untpd.Match, sel: Tree, wideSelType: Type, cases: List[untpd.CaseDef], pt: Type)(using Context) = if (!tree.isInline || ctx.owner.isInlineMethod) // don't reduce match of nested inline method yet From 99014e035a6743af56675bc9db3c6ecc674f6ac9 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 28 Jan 2021 16:01:59 +0100 Subject: [PATCH 19/56] Split conditions to run Staging and PickleQuotes --- compiler/src/dotty/tools/dotc/CompilationUnit.scala | 12 +++++++++--- .../src/dotty/tools/dotc/transform/Inlining.scala | 1 + .../dotty/tools/dotc/transform/PickleQuotes.scala | 2 +- .../src/dotty/tools/dotc/transform/PostTyper.scala | 1 + compiler/src/dotty/tools/dotc/typer/Inliner.scala | 1 + 5 files changed, 13 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/CompilationUnit.scala b/compiler/src/dotty/tools/dotc/CompilationUnit.scala index d6a084fcc00f..d377b7f42d30 100644 --- a/compiler/src/dotty/tools/dotc/CompilationUnit.scala +++ b/compiler/src/dotty/tools/dotc/CompilationUnit.scala @@ -43,6 +43,11 @@ class CompilationUnit protected (val source: SourceFile) { */ var needsStaging: Boolean = false + /** Will be set to `true` if contains `Quote` that needs to be pickled + * The information is used in phase `PickleQuotes` in order to avoid traversing trees that need no transformations. + */ + var needsQuotePickling: Boolean = false + /** A structure containing a temporary map for generating inline accessors */ val inlineAccessors: InlineAccessors = new InlineAccessors @@ -93,7 +98,8 @@ object CompilationUnit { if (forceTrees) { val force = new Force force.traverse(unit1.tpdTree) - unit1.needsStaging = force.needsStaging + unit1.needsStaging = force.containsQuote + unit1.needsQuotePickling = force.containsQuote } unit1 } @@ -119,10 +125,10 @@ object CompilationUnit { /** Force the tree to be loaded */ private class Force extends TreeTraverser { - var needsStaging = false + var containsQuote = false def traverse(tree: Tree)(using Context): Unit = { if (tree.symbol.isQuote) - needsStaging = true + containsQuote = true traverseChildren(tree) } } diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala index 471ba38f0e11..696543f033d7 100644 --- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala @@ -92,6 +92,7 @@ class Inlining extends MacroTransform { else Inliner.inlineCall(tree1) case _: GenericApply if tree.symbol.isQuote => ctx.compilationUnit.needsStaging = true + ctx.compilationUnit.needsQuotePickling = true super.transform(tree)(using StagingContext.quoteContext) case _: GenericApply if tree.symbol.isExprSplice => super.transform(tree)(using StagingContext.spliceContext) diff --git a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala index 0f00add41809..e0f0858652cf 100644 --- a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala @@ -88,7 +88,7 @@ class PickleQuotes extends MacroTransform { } override def run(using Context): Unit = - if (ctx.compilationUnit.needsStaging) super.run(using freshStagingContext) + if (ctx.compilationUnit.needsQuotePickling) super.run(using freshStagingContext) protected def newTransformer(using Context): Transformer = new Transformer { override def transform(tree: tpd.Tree)(using Context): tpd.Tree = diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 09806b2b1679..78d7bfa0ba63 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -302,6 +302,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase case tree: TypeApply => if tree.symbol.isQuote then ctx.compilationUnit.needsStaging = true + ctx.compilationUnit.needsQuotePickling = true val tree1 @ TypeApply(fn, args) = normalizeTypeArgs(tree) args.foreach(checkInferredWellFormed) if (fn.symbol != defn.ChildAnnot.primaryConstructor) diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index ce3b782d5ab3..ba5e072b99a3 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -1295,6 +1295,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) { } if res.symbol == defn.QuotedRuntime_exprQuote then ctx.compilationUnit.needsStaging = true + ctx.compilationUnit.needsQuotePickling = true res override def typedTypeApply(tree: untpd.TypeApply, pt: Type)(using Context): Tree = From 077d36465fb4a7bd5885b97f1c29d4808da0638a Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 28 Jan 2021 16:13:44 +0100 Subject: [PATCH 20/56] Avoid running PickleQuotes when possible --- compiler/src/dotty/tools/dotc/transform/Inlining.scala | 4 ++-- compiler/src/dotty/tools/dotc/typer/Inliner.scala | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala index 696543f033d7..5a53fdb6fda2 100644 --- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala @@ -91,8 +91,8 @@ class Inlining extends MacroTransform { if tree1.tpe.isError then tree1 else Inliner.inlineCall(tree1) case _: GenericApply if tree.symbol.isQuote => - ctx.compilationUnit.needsStaging = true - ctx.compilationUnit.needsQuotePickling = true + if level == 0 then + ctx.compilationUnit.needsQuotePickling = true super.transform(tree)(using StagingContext.quoteContext) case _: GenericApply if tree.symbol.isExprSplice => super.transform(tree)(using StagingContext.spliceContext) diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index ba5e072b99a3..ba0a07f42087 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -1294,7 +1294,6 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) { inlineIfIsNestedInlineCall(res) } if res.symbol == defn.QuotedRuntime_exprQuote then - ctx.compilationUnit.needsStaging = true ctx.compilationUnit.needsQuotePickling = true res From 7085eee6d0c344ff8f3a93caa57b888daba68345 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 28 Jan 2021 16:56:44 +0100 Subject: [PATCH 21/56] Patch tuples --- library/src/scala/Tuple.scala | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/library/src/scala/Tuple.scala b/library/src/scala/Tuple.scala index 87d6f26e0e6f..72e156db9139 100644 --- a/library/src/scala/Tuple.scala +++ b/library/src/scala/Tuple.scala @@ -255,17 +255,20 @@ sealed trait NonEmptyTuple extends Tuple { /** Get the i-th element of this tuple. * Equivalent to productElement but with a precise return type. */ - inline def apply[This >: this.type <: NonEmptyTuple](n: Int): Elem[This, n.type] = + // FIXME remove transparent + transparent inline def apply[This >: this.type <: NonEmptyTuple](n: Int): Elem[This, n.type] = runtime.Tuples.apply(this, n).asInstanceOf[Elem[This, n.type]] /** Get the head of this tuple */ - inline def head[This >: this.type <: NonEmptyTuple]: Head[This] = + // FIXME remove transparent + transparent inline def head[This >: this.type <: NonEmptyTuple]: Head[This] = runtime.Tuples.apply(this, 0).asInstanceOf[Head[This]] /** Get the tail of this tuple. * This operation is O(this.size) */ - inline def tail[This >: this.type <: NonEmptyTuple]: Tail[This] = + // FIXME remove transparent + transparent inline def tail[This >: this.type <: NonEmptyTuple]: Tail[This] = runtime.Tuples.tail(this).asInstanceOf[Tail[This]] } From 1f4e3e631be03fd8e6f69134a60ea0db47ef4257 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 29 Jan 2021 11:01:00 +0100 Subject: [PATCH 22/56] Add valueof to from-tasty blacklist --- compiler/test/dotc/run-from-tasty.blacklist | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/test/dotc/run-from-tasty.blacklist b/compiler/test/dotc/run-from-tasty.blacklist index e69de29bb2d1..2c483e9e34b6 100644 --- a/compiler/test/dotc/run-from-tasty.blacklist +++ b/compiler/test/dotc/run-from-tasty.blacklist @@ -0,0 +1,2 @@ +# CI only: cannot reduce summonFrom with +sip23-valueof.scala From 04989b16497bc17d29749f2bdfea5958ae664637 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 29 Jan 2021 11:58:31 +0100 Subject: [PATCH 23/56] Adapt tests --- .gitignore | 4 ++++ compiler/test-resources/repl/i5218 | 2 +- tests/semanticdb/expect/recursion.expect.scala | 2 +- tests/semanticdb/expect/recursion.scala | 2 +- tests/semanticdb/metac.expect | 8 ++++---- 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index ba47c2dd4cb3..3cdfd112b86d 100644 --- a/.gitignore +++ b/.gitignore @@ -89,5 +89,9 @@ community-build/sbt-scalajs-sbt *.check.out !/dist/bin/ +# semanticdb test output files +*.expect.scala.out +*.expect.out + # Bloop .bsp diff --git a/compiler/test-resources/repl/i5218 b/compiler/test-resources/repl/i5218 index abe63009ef74..e99590d0315d 100644 --- a/compiler/test-resources/repl/i5218 +++ b/compiler/test-resources/repl/i5218 @@ -3,4 +3,4 @@ val tuple: (Int, String, Long) = (1,2,3) scala> 0.0 *: tuple val res0: (Double, Int, String, Long) = (0.0,1,2,3) scala> tuple ++ tuple -val res1: Int *: String *: Long *: tuple.type = (1,2,3,1,2,3) +val res1: Int *: scala.Tuple.Concat[(String, Long), tuple.type] = (1,2,3,1,2,3) diff --git a/tests/semanticdb/expect/recursion.expect.scala b/tests/semanticdb/expect/recursion.expect.scala index a576eca7a443..aba74b1b032e 100644 --- a/tests/semanticdb/expect/recursion.expect.scala +++ b/tests/semanticdb/expect/recursion.expect.scala @@ -3,7 +3,7 @@ package recursion object Nats/*<-recursion::Nats.*/ { sealed trait Nat/*<-recursion::Nats.Nat#*/ { - inline def ++/*<-recursion::Nats.Nat#`++`().*/ : Succ/*->recursion::Nats.Succ#*/[this.type] = Succ/*->recursion::Nats.Succ.*//*->recursion::Nats.Succ.apply().*/(this) + transparent inline def ++/*<-recursion::Nats.Nat#`++`().*/ : Succ/*->recursion::Nats.Succ#*/[this.type] = Succ/*->recursion::Nats.Succ.*//*->recursion::Nats.Succ.apply().*/(this) transparent inline def +/*<-recursion::Nats.Nat#`+`().*/(inline that/*<-recursion::Nats.Nat#`+`().(that)*/: Nat/*->recursion::Nats.Nat#*/): Nat/*->recursion::Nats.Nat#*/ = inline this match { diff --git a/tests/semanticdb/expect/recursion.scala b/tests/semanticdb/expect/recursion.scala index 4c0b54dd5083..3c06e5b202f4 100644 --- a/tests/semanticdb/expect/recursion.scala +++ b/tests/semanticdb/expect/recursion.scala @@ -3,7 +3,7 @@ package recursion object Nats { sealed trait Nat { - inline def ++ : Succ[this.type] = Succ(this) + transparent inline def ++ : Succ[this.type] = Succ(this) transparent inline def +(inline that: Nat): Nat = inline this match { diff --git a/tests/semanticdb/metac.expect b/tests/semanticdb/metac.expect index c13392fb7ebc..191e2f3d0379 100644 --- a/tests/semanticdb/metac.expect +++ b/tests/semanticdb/metac.expect @@ -3309,10 +3309,10 @@ Occurrences: [3:7..3:11): Nats <- recursion/Nats. [4:15..4:18): Nat <- recursion/Nats.Nat# [5:4..5:4): <- recursion/Nats.Nat#``(). -[5:15..5:17): ++ <- recursion/Nats.Nat#`++`(). -[5:20..5:24): Succ -> recursion/Nats.Succ# -[5:38..5:42): Succ -> recursion/Nats.Succ. -[5:42..5:42): -> recursion/Nats.Succ.apply(). +[5:27..5:29): ++ <- recursion/Nats.Nat#`++`(). +[5:32..5:36): Succ -> recursion/Nats.Succ# +[5:50..5:54): Succ -> recursion/Nats.Succ. +[5:54..5:54): -> recursion/Nats.Succ.apply(). [7:27..7:28): + <- recursion/Nats.Nat#`+`(). [7:36..7:40): that <- recursion/Nats.Nat#`+`().(that) [7:42..7:45): Nat -> recursion/Nats.Nat# From bd41f01239e68134410a945f4753ce822bedf351 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 29 Jan 2021 14:30:23 +0100 Subject: [PATCH 24/56] Patch test --- .../src/main/scala/dotty/tools/benchmarks/tuples/TupleOps.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/TupleOps.scala b/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/TupleOps.scala index fa6fb63e73ad..8f4e451e7dc5 100644 --- a/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/TupleOps.scala +++ b/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/TupleOps.scala @@ -50,7 +50,7 @@ class TupleOps { def tupleMergeSort(tuple: Tuple): Tuple = if (tuple.size <= 1) tuple else { - val (tuple1, tuple2) = tuple.splitAt(tuple.size / 2) + val (tuple1, tuple2) = tuple.splitAt(tuple.size / 2): (Tuple, Tuple) // FIXME remove ascription val (sorted1, sorted2) = (tupleMergeSort(tuple1), tupleMergeSort(tuple2)) tupleMerge(sorted1, sorted2) } From 5e489eada23b34670be0d0b76ed36ad1fde3f562 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 29 Jan 2021 16:11:38 +0100 Subject: [PATCH 25/56] Document failures --- .../main/scala/dotty/tools/benchmarks/tuples/TupleOps.scala | 2 +- library/src/scala/Tuple.scala | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/TupleOps.scala b/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/TupleOps.scala index 8f4e451e7dc5..fa6fb63e73ad 100644 --- a/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/TupleOps.scala +++ b/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/TupleOps.scala @@ -50,7 +50,7 @@ class TupleOps { def tupleMergeSort(tuple: Tuple): Tuple = if (tuple.size <= 1) tuple else { - val (tuple1, tuple2) = tuple.splitAt(tuple.size / 2): (Tuple, Tuple) // FIXME remove ascription + val (tuple1, tuple2) = tuple.splitAt(tuple.size / 2) val (sorted1, sorted2) = (tupleMergeSort(tuple1), tupleMergeSort(tuple2)) tupleMerge(sorted1, sorted2) } diff --git a/library/src/scala/Tuple.scala b/library/src/scala/Tuple.scala index 72e156db9139..4abb07b53554 100644 --- a/library/src/scala/Tuple.scala +++ b/library/src/scala/Tuple.scala @@ -70,7 +70,9 @@ sealed trait Tuple extends Product { * consisting of the first n elements, and the tuple `(an+1, ..., am)` consisting * of the remaining elements. */ - inline def splitAt[This >: this.type <: Tuple](n: Int): Split[This, n.type] = + // FIXME remove transparent + // failure in bench-run/src/main/scala/dotty/tools/benchmarks/tuples/TupleOps.scala + transparent inline def splitAt[This >: this.type <: Tuple](n: Int): Split[This, n.type] = runtime.Tuples.splitAt(this, n).asInstanceOf[Split[This, n.type]] } @@ -268,6 +270,8 @@ sealed trait NonEmptyTuple extends Tuple { * This operation is O(this.size) */ // FIXME remove transparent + // fails in tests/run/tuples1a.scala + // https://github.com/lampepfl/dotty/issues/11236 transparent inline def tail[This >: this.type <: NonEmptyTuple]: Tail[This] = runtime.Tuples.tail(this).asInstanceOf[Tail[This]] From b850f2a287d82ca23e240c156e227f3b7c20e68b Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 29 Jan 2021 16:16:56 +0100 Subject: [PATCH 26/56] Remove some transparent form Tuple --- library/src/scala/Tuple.scala | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/library/src/scala/Tuple.scala b/library/src/scala/Tuple.scala index 4abb07b53554..6e3baf92dbaa 100644 --- a/library/src/scala/Tuple.scala +++ b/library/src/scala/Tuple.scala @@ -257,22 +257,17 @@ sealed trait NonEmptyTuple extends Tuple { /** Get the i-th element of this tuple. * Equivalent to productElement but with a precise return type. */ - // FIXME remove transparent - transparent inline def apply[This >: this.type <: NonEmptyTuple](n: Int): Elem[This, n.type] = + inline def apply[This >: this.type <: NonEmptyTuple](n: Int): Elem[This, n.type] = runtime.Tuples.apply(this, n).asInstanceOf[Elem[This, n.type]] /** Get the head of this tuple */ - // FIXME remove transparent - transparent inline def head[This >: this.type <: NonEmptyTuple]: Head[This] = + inline def head[This >: this.type <: NonEmptyTuple]: Head[This] = runtime.Tuples.apply(this, 0).asInstanceOf[Head[This]] /** Get the tail of this tuple. * This operation is O(this.size) */ - // FIXME remove transparent - // fails in tests/run/tuples1a.scala - // https://github.com/lampepfl/dotty/issues/11236 - transparent inline def tail[This >: this.type <: NonEmptyTuple]: Tail[This] = + inline def tail[This >: this.type <: NonEmptyTuple]: Tail[This] = runtime.Tuples.tail(this).asInstanceOf[Tail[This]] } From 4953991b54ea33f7846d1d66da9a3e8efe8ab10c Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 29 Jan 2021 17:05:43 +0100 Subject: [PATCH 27/56] Workaround #11247 --- .../main/scala/dotty/tools/benchmarks/tuples/TupleOps.scala | 2 +- library/src/scala/Tuple.scala | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/TupleOps.scala b/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/TupleOps.scala index fa6fb63e73ad..3a1de4b88201 100644 --- a/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/TupleOps.scala +++ b/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/TupleOps.scala @@ -50,7 +50,7 @@ class TupleOps { def tupleMergeSort(tuple: Tuple): Tuple = if (tuple.size <= 1) tuple else { - val (tuple1, tuple2) = tuple.splitAt(tuple.size / 2) + val (tuple1: Tuple, tuple2: Tuple) = tuple.splitAt(tuple.size / 2) // TODO remove ascriptions when #11247 is fixed val (sorted1, sorted2) = (tupleMergeSort(tuple1), tupleMergeSort(tuple2)) tupleMerge(sorted1, sorted2) } diff --git a/library/src/scala/Tuple.scala b/library/src/scala/Tuple.scala index 6e3baf92dbaa..87d6f26e0e6f 100644 --- a/library/src/scala/Tuple.scala +++ b/library/src/scala/Tuple.scala @@ -70,9 +70,7 @@ sealed trait Tuple extends Product { * consisting of the first n elements, and the tuple `(an+1, ..., am)` consisting * of the remaining elements. */ - // FIXME remove transparent - // failure in bench-run/src/main/scala/dotty/tools/benchmarks/tuples/TupleOps.scala - transparent inline def splitAt[This >: this.type <: Tuple](n: Int): Split[This, n.type] = + inline def splitAt[This >: this.type <: Tuple](n: Int): Split[This, n.type] = runtime.Tuples.splitAt(this, n).asInstanceOf[Split[This, n.type]] } From 90e5733c712aad12aa86bfd3197e3f024175ddad Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 Feb 2021 09:37:56 +0100 Subject: [PATCH 28/56] Move i7580.scala to tests/pos-deep-subtype --- tests/{pos => pos-deep-subtype}/i7580.scala | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/{pos => pos-deep-subtype}/i7580.scala (100%) diff --git a/tests/pos/i7580.scala b/tests/pos-deep-subtype/i7580.scala similarity index 100% rename from tests/pos/i7580.scala rename to tests/pos-deep-subtype/i7580.scala From 2f4ee75931a4ce44da49301d1f24298abd9310e2 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 Feb 2021 09:46:11 +0100 Subject: [PATCH 29/56] Update TupleOps TODO message --- .../src/main/scala/dotty/tools/benchmarks/tuples/TupleOps.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/TupleOps.scala b/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/TupleOps.scala index 3a1de4b88201..f78728b6af54 100644 --- a/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/TupleOps.scala +++ b/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/TupleOps.scala @@ -50,7 +50,7 @@ class TupleOps { def tupleMergeSort(tuple: Tuple): Tuple = if (tuple.size <= 1) tuple else { - val (tuple1: Tuple, tuple2: Tuple) = tuple.splitAt(tuple.size / 2) // TODO remove ascriptions when #11247 is fixed + val (tuple1: Tuple, tuple2: Tuple) = tuple.splitAt(tuple.size / 2) // TODO remove ascriptions (issue with type variable constraints) val (sorted1, sorted2) = (tupleMergeSort(tuple1), tupleMergeSort(tuple2)) tupleMerge(sorted1, sorted2) } From f96ced39145646c953e53c1b4ef15d175e6247ad Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 Feb 2021 10:11:30 +0100 Subject: [PATCH 30/56] Update inlinr givens in shapeless --- community-build/community-projects/shapeless | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/community-build/community-projects/shapeless b/community-build/community-projects/shapeless index 3058735a54a2..56bcb5246bca 160000 --- a/community-build/community-projects/shapeless +++ b/community-build/community-projects/shapeless @@ -1 +1 @@ -Subproject commit 3058735a54a23df67246ecce5b09f6a6cd3dfaec +Subproject commit 56bcb5246bca8099d12fa54379ece6fb440657ec From 1333adf4c9d43a68d70154781751ddcdb8ebefc9 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 Feb 2021 11:37:38 +0100 Subject: [PATCH 31/56] List failing CommunityBuildTestA tests --- .../dotty/communitybuild/CommunityBuildTest.scala | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala b/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala index 0154369438a6..bdc073bc5d31 100644 --- a/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala +++ b/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala @@ -152,3 +152,13 @@ class CommunityBuildTestB extends CommunityBuildTest: end CommunityBuildTestB class TestCategory + +// FIXME +// ******************************************************************************** +// Failed projects:-build / Test / testOnly 3569s +// - dotty.communitybuild.CommunityBuildTestA.scalatest +// - dotty.communitybuild.CommunityBuildTestA.upickle +// - dotty.communitybuild.CommunityBuildTestA.oslib +// - dotty.communitybuild.CommunityBuildTestA.utest +// - dotty.communitybuild.CommunityBuildTestA.izumiReflect +// ******************************************************************************** \ No newline at end of file From fc04bcdce5912c2fb73bd33bb4ca46f48d0a2009 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 Feb 2021 14:36:55 +0100 Subject: [PATCH 32/56] Move type ascription --- .../src/main/scala/dotty/tools/benchmarks/tuples/TupleOps.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/TupleOps.scala b/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/TupleOps.scala index f78728b6af54..ac96509d233d 100644 --- a/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/TupleOps.scala +++ b/bench-run/src/main/scala/dotty/tools/benchmarks/tuples/TupleOps.scala @@ -50,7 +50,7 @@ class TupleOps { def tupleMergeSort(tuple: Tuple): Tuple = if (tuple.size <= 1) tuple else { - val (tuple1: Tuple, tuple2: Tuple) = tuple.splitAt(tuple.size / 2) // TODO remove ascriptions (issue with type variable constraints) + val (tuple1, tuple2) = tuple.splitAt(tuple.size / 2): (Tuple, Tuple)// TODO remove ascriptions (issue with type variable constraints) val (sorted1, sorted2) = (tupleMergeSort(tuple1), tupleMergeSort(tuple2)) tupleMerge(sorted1, sorted2) } From 08586121441d896b124f19dc68e55fbd02859cc4 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 Feb 2021 15:14:07 +0100 Subject: [PATCH 33/56] Add regression test --- compiler/test/dotc/run-test-pickling.blacklist | 1 + tests/run/typeCheckErrors.check | 1 + tests/run/typeCheckErrors.scala | 8 ++++++++ 3 files changed, 10 insertions(+) create mode 100644 tests/run/typeCheckErrors.check create mode 100644 tests/run/typeCheckErrors.scala diff --git a/compiler/test/dotc/run-test-pickling.blacklist b/compiler/test/dotc/run-test-pickling.blacklist index c997114e3015..d36314e896d8 100644 --- a/compiler/test/dotc/run-test-pickling.blacklist +++ b/compiler/test/dotc/run-test-pickling.blacklist @@ -22,6 +22,7 @@ tuple-zip.scala tuples1.scala tuples1a.scala tuples1b.scala +typeCheckErrors.scala typeclass-derivation-doc-example.scala typeclass-derivation1.scala typeclass-derivation2.scala diff --git a/tests/run/typeCheckErrors.check b/tests/run/typeCheckErrors.check new file mode 100644 index 000000000000..80202799dd46 --- /dev/null +++ b/tests/run/typeCheckErrors.check @@ -0,0 +1 @@ +List(Error(value check is not a member of Unit,compileError("1" * 2).check(""),22,Typer), Error(argument to compileError must be a statically known String,compileError("1" * 2).check(""),13,Typer)) diff --git a/tests/run/typeCheckErrors.scala b/tests/run/typeCheckErrors.scala new file mode 100644 index 000000000000..9fd357e5b3d2 --- /dev/null +++ b/tests/run/typeCheckErrors.scala @@ -0,0 +1,8 @@ +import scala.compiletime.testing._ + +transparent inline def compileError(inline expr: String): Unit = + println(typeCheckErrors(expr)) + +@main def Test = compileError( + """compileError("1" * 2).check("")""" +) From c455e84ca2dc013739c16dec441e625cc7ec577c Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 Feb 2021 15:38:46 +0100 Subject: [PATCH 34/56] Fix position of typeCheckErrors --- community-build/community-projects/utest | 2 +- compiler/src/dotty/tools/dotc/typer/Inliner.scala | 4 ++-- tests/run/typeCheckErrors.check | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/community-build/community-projects/utest b/community-build/community-projects/utest index 654376f41f83..cf30b7c40425 160000 --- a/community-build/community-projects/utest +++ b/community-build/community-projects/utest @@ -1 +1 @@ -Subproject commit 654376f41f8380a44fdb70f693016f25633ae1be +Subproject commit cf30b7c40425d4d3809eef462888e748bff0c809 diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index ba0a07f42087..82c0ad42917f 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -288,8 +288,8 @@ object Inliner { } val Apply(_, codeArg :: Nil) = tree + val codeArg1 = stripTyped(codeArg.underlying) val underlyingCodeArg = - val codeArg1 = stripTyped(codeArg.underlying) if Inliner.isInlineable(codeArg1) then stripTyped(Inliner.inlineCall(codeArg1)) else codeArg1 @@ -308,7 +308,7 @@ object Inliner { res ++= typerErrors.map(e => ErrorKind.Typer -> e) res.toList case t => - report.error(em"argument to ${tree.symbol} must be a statically known String but was: $codeArg", codeArg.srcPos) + report.error(em"argument to compileError must be a statically known String but was: $codeArg", codeArg1.srcPos) Nil } diff --git a/tests/run/typeCheckErrors.check b/tests/run/typeCheckErrors.check index 80202799dd46..c74d3695926d 100644 --- a/tests/run/typeCheckErrors.check +++ b/tests/run/typeCheckErrors.check @@ -1 +1 @@ -List(Error(value check is not a member of Unit,compileError("1" * 2).check(""),22,Typer), Error(argument to compileError must be a statically known String,compileError("1" * 2).check(""),13,Typer)) +List(Error(value check is not a member of Unit,compileError("1" * 2).check(""),22,Typer), Error(argument to compileError must be a statically known String but was: augmentString("1").*(2),compileError("1" * 2).check(""),13,Typer)) From caa1a79a86503a254768a9e6fa4e65a96aa35ea0 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 Feb 2021 17:54:49 +0100 Subject: [PATCH 35/56] Fix utest tesing macro --- community-build/community-projects/utest | 2 +- .../test/scala/dotty/communitybuild/CommunityBuildTest.scala | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/community-build/community-projects/utest b/community-build/community-projects/utest index cf30b7c40425..64fc7b243980 160000 --- a/community-build/community-projects/utest +++ b/community-build/community-projects/utest @@ -1 +1 @@ -Subproject commit cf30b7c40425d4d3809eef462888e748bff0c809 +Subproject commit 64fc7b243980e87fac573fc831552eea70abb2b3 diff --git a/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala b/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala index bdc073bc5d31..032d75d7aee2 100644 --- a/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala +++ b/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala @@ -157,8 +157,5 @@ class TestCategory // ******************************************************************************** // Failed projects:-build / Test / testOnly 3569s // - dotty.communitybuild.CommunityBuildTestA.scalatest -// - dotty.communitybuild.CommunityBuildTestA.upickle -// - dotty.communitybuild.CommunityBuildTestA.oslib -// - dotty.communitybuild.CommunityBuildTestA.utest // - dotty.communitybuild.CommunityBuildTestA.izumiReflect // ******************************************************************************** \ No newline at end of file From 7e40bb26743958a4dae83738e2812effb372b978 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 2 Feb 2021 08:30:38 +0100 Subject: [PATCH 36/56] Update izumi-reflect --- community-build/community-projects/izumi-reflect | 2 +- .../scala/dotty/communitybuild/CommunityBuildTest.scala | 7 ------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/community-build/community-projects/izumi-reflect b/community-build/community-projects/izumi-reflect index c368dcc9025b..1a435f33ab06 160000 --- a/community-build/community-projects/izumi-reflect +++ b/community-build/community-projects/izumi-reflect @@ -1 +1 @@ -Subproject commit c368dcc9025b455944e797dffa1b78d3ecf83752 +Subproject commit 1a435f33ab067374785d475317f7ad287d837cb1 diff --git a/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala b/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala index 032d75d7aee2..0154369438a6 100644 --- a/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala +++ b/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala @@ -152,10 +152,3 @@ class CommunityBuildTestB extends CommunityBuildTest: end CommunityBuildTestB class TestCategory - -// FIXME -// ******************************************************************************** -// Failed projects:-build / Test / testOnly 3569s -// - dotty.communitybuild.CommunityBuildTestA.scalatest -// - dotty.communitybuild.CommunityBuildTestA.izumiReflect -// ******************************************************************************** \ No newline at end of file From b6371ea191ea145a424fdef54de9e8e36e58aeb5 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 2 Feb 2021 09:03:48 +0100 Subject: [PATCH 37/56] Update scalatest --- community-build/community-projects/scalatest | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/community-build/community-projects/scalatest b/community-build/community-projects/scalatest index 69e29ed19e6f..e58ec52f69df 160000 --- a/community-build/community-projects/scalatest +++ b/community-build/community-projects/scalatest @@ -1 +1 @@ -Subproject commit 69e29ed19e6fe12e9acf6f748d0693bdd957cfd0 +Subproject commit e58ec52f69df080972a93386f849f33f18e83596 From 0206a6e40385d9afd44e27e23668ad1648704537 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 2 Feb 2021 10:30:10 +0100 Subject: [PATCH 38/56] Update docs --- docs/docs/reference/metaprogramming/inline.md | 17 +++++++++++++++++ docs/docs/reference/metaprogramming/macros.md | 4 ++-- .../src/scala/compiletime/testing/package.scala | 4 ++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/docs/docs/reference/metaprogramming/inline.md b/docs/docs/reference/metaprogramming/inline.md index ba18f55d76d8..b798b952f80d 100644 --- a/docs/docs/reference/metaprogramming/inline.md +++ b/docs/docs/reference/metaprogramming/inline.md @@ -284,6 +284,21 @@ transparent inline def zero: Int = 0 val one: 1 = zero + 1 ``` +### Transparent vs. non-transparent inline +As we already discussed, transparent inline methods will provide a more precise. +Technically this implies that transparent inline methods must be expanded while typing the program. Other inline methods are inlined later after the program is fully typed. + +For example the following two functions will be typed the same way but will be inlined at different times. +```scala +inline def f1: T = ... +transparent inline def f2: T = (...): T +``` + +A noteworthy difference is the behavior of `transparent inline given`. +If there is an error reported when inlining that definition, it will be considered as an implicit search mismatch and the search will continue. +A `transparent inline given` can add a type ascription in its RHS (as in `f2` from the previous example) to avoid the precise type but keep the search behavior. +On the other hand `inline given` be taken as the implicit and then after typing is done the code is inlined and any error will be emitted as usual. + ## Inline Conditionals An if-then-else expression whose condition is a constant expression can be simplified to @@ -313,6 +328,8 @@ below: | This location is in code that was inlined at ... ``` +In a transparent inline, an `inline if` will force the inlining of any inline definition in its condition. + ## Inline Matches A `match` expression in the body of an `inline` method definition may be diff --git a/docs/docs/reference/metaprogramming/macros.md b/docs/docs/reference/metaprogramming/macros.md index 997252176fc3..fc76f1607151 100644 --- a/docs/docs/reference/metaprogramming/macros.md +++ b/docs/docs/reference/metaprogramming/macros.md @@ -618,9 +618,9 @@ def setForExpr[T: Type](using Quotes): Expr[Set[T]] = case _ => '{ new HashSet[T] } ``` -## Relationship with Whitebox Inline +## Relationship with Transparent Inline -[Inline](./inline.md) documents inlining. The code below introduces a whitebox +[Inline](./inline.md) documents inlining. The code below introduces a transparent inline method that can calculate either a value of type `Int` or a value of type `String`. diff --git a/library/src/scala/compiletime/testing/package.scala b/library/src/scala/compiletime/testing/package.scala index 0dc34ade6518..ebe629244fb3 100644 --- a/library/src/scala/compiletime/testing/package.scala +++ b/library/src/scala/compiletime/testing/package.scala @@ -2,6 +2,8 @@ package scala.compiletime package testing /** Whether the code type checks in the current context? + * + * An inline definition with a call to `typeChecks` should be transparent. * * @param code The code to be type checked * @@ -20,6 +22,8 @@ transparent inline def typeChecks(inline code: String): Boolean = * version to version. This API is to be used for testing purposes * only. * + * An inline definition with a call to `typeCheckErrors` should be transparent. + * * @param code The code to be type checked * * @return a list of errors encountered during parsing and typechecking. From 6251b0f755069ffd36e649b39aece06ccdf1f695 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 2 Feb 2021 11:05:52 +0100 Subject: [PATCH 39/56] Update izumi-reflect --- community-build/community-projects/izumi-reflect | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/community-build/community-projects/izumi-reflect b/community-build/community-projects/izumi-reflect index 1a435f33ab06..9a332c9011bb 160000 --- a/community-build/community-projects/izumi-reflect +++ b/community-build/community-projects/izumi-reflect @@ -1 +1 @@ -Subproject commit 1a435f33ab067374785d475317f7ad287d837cb1 +Subproject commit 9a332c9011bb018a4b36f582f30e53966c6f5219 From 72765f9e73fec257948aac650fa0c2d05c4926e3 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 2 Feb 2021 11:43:38 +0100 Subject: [PATCH 40/56] Temporarely disable izumiReflect --- .../test/scala/dotty/communitybuild/CommunityBuildTest.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala b/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala index 0154369438a6..d9662c4ebd65 100644 --- a/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala +++ b/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala @@ -86,7 +86,7 @@ class CommunityBuildTestA extends CommunityBuildTest: @Test def fansi = projects.fansi.run() @Test def fastparse = projects.fastparse.run() @Test def geny = projects.geny.run() - @Test def izumiReflect = projects.izumiReflect.run() + @Ignore @Test def izumiReflect = projects.izumiReflect.run() @Test def oslib = projects.oslib.run() // @Test def oslibWatch = projects.oslibWatch.run() @Test def pprint = projects.pprint.run() From c17e82a69d68fa54616b99b71e9bb55a4f51d37b Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 2 Feb 2021 12:17:18 +0100 Subject: [PATCH 41/56] Re-enable sbt-dotty tests --- .../source-dependencies/macro-expansion-dependencies-1/test | 4 ++-- .../source-dependencies/macro-expansion-dependencies-2/test | 2 +- .../source-dependencies/macro-expansion-dependencies-3/test | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-1/test b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-1/test index 63b0650e66d5..4b6cf56e68be 100644 --- a/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-1/test +++ b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-1/test @@ -13,11 +13,11 @@ $ copy-file changes/Macro.scala Macro.scala # use an implemntation of the macro that emits a compile-time error $ copy-file changes/MacroCompileError.scala Macro.scala -# FIXME -> compile +-> compile $ copy-file changes/Macro.scala Macro.scala > clean > compile $ copy-file changes/MacroRuntimeError.scala Macro.scala -# FIXME -> run +-> run diff --git a/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-2/test b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-2/test index ccec76af3d2f..10cfbcb78b6c 100644 --- a/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-2/test +++ b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-2/test @@ -11,7 +11,7 @@ $ copy-file changes/MacroRuntime.scala MacroRuntime.scala > clean > run -# use an implemntation of the macro that emits a compile-time error +# use an implemntation of the macro that emits a compile time error $ copy-file changes/MacroRuntimeCompileError.scala MacroRuntime.scala -> compile diff --git a/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-3/test b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-3/test index bc0040ffc4c4..b743359dcdc7 100644 --- a/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-3/test +++ b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-3/test @@ -3,4 +3,4 @@ # make sure that Macros is recompiled due to it's dependencie on B.f # this will end in a failure to compile due to cyclic macros $ copy-file changes/TestB.scala TestB.scala -# FIXME -> compile +-> compile From 1b7dcd18b9f5dc5d4920b1dab2d147445988b2d7 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 2 Feb 2021 13:20:50 +0100 Subject: [PATCH 42/56] Only run inlining phase when needed --- compiler/src/dotty/tools/dotc/CompilationUnit.scala | 9 +++++++++ compiler/src/dotty/tools/dotc/transform/Inlining.scala | 5 +++-- compiler/src/dotty/tools/dotc/transform/PostTyper.scala | 8 ++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/CompilationUnit.scala b/compiler/src/dotty/tools/dotc/CompilationUnit.scala index d377b7f42d30..a4f219c88510 100644 --- a/compiler/src/dotty/tools/dotc/CompilationUnit.scala +++ b/compiler/src/dotty/tools/dotc/CompilationUnit.scala @@ -38,6 +38,11 @@ class CompilationUnit protected (val source: SourceFile) { */ val freshNames: FreshNameCreator = new FreshNameCreator.Default + /** Will be set to `true` if there are inline call that must be inlined after typer. + * The information is used in phase `Inlining` in order to avoid traversing trees that need no transformations. + */ + var needsInlining: Boolean = false + /** Will be set to `true` if contains `Quote`. * The information is used in phase `Staging` in order to avoid traversing trees that need no transformations. */ @@ -100,6 +105,7 @@ object CompilationUnit { force.traverse(unit1.tpdTree) unit1.needsStaging = force.containsQuote unit1.needsQuotePickling = force.containsQuote + unit1.needsInlining = force.containsInline } unit1 } @@ -126,9 +132,12 @@ object CompilationUnit { /** Force the tree to be loaded */ private class Force extends TreeTraverser { var containsQuote = false + var containsInline = false def traverse(tree: Tree)(using Context): Unit = { if (tree.symbol.isQuote) containsQuote = true + if tree.symbol.is(Flags.Inline) then + containsInline = true traverseChildren(tree) } } diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala index 5a53fdb6fda2..ce2e8355237e 100644 --- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala @@ -40,8 +40,9 @@ class Inlining extends MacroTransform { override def allowsImplicitSearch: Boolean = true override def run(using Context): Unit = - try super.run - catch case _: CompilationUnit.SuspendException => () + if ctx.compilationUnit.needsInlining then + try super.run + catch case _: CompilationUnit.SuspendException => () override def runOn(units: List[CompilationUnit])(using Context): List[CompilationUnit] = val newUnits = super.runOn(units).filterNot(_.suspended) diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 78d7bfa0ba63..3ebaa15d71bc 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -258,12 +258,16 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase override def transform(tree: Tree)(using Context): Tree = try tree match { case tree: Ident if !tree.isType => + if tree.symbol.is(Inline) then + ctx.compilationUnit.needsInlining = true checkNoConstructorProxy(tree) tree.tpe match { case tpe: ThisType => This(tpe.cls).withSpan(tree.span) case _ => tree } case tree @ Select(qual, name) => + if tree.symbol.is(Inline) then + ctx.compilationUnit.needsInlining = true if (name.isTypeName) { Checking.checkRealizable(qual.tpe, qual.srcPos) withMode(Mode.Type)(super.transform(tree)) @@ -272,6 +276,8 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase checkNoConstructorProxy(tree) transformSelect(tree, Nil) case tree: Apply => + if tree.symbol.is(Inline) then + ctx.compilationUnit.needsInlining = true val methType = tree.fun.tpe.widen val app = if (methType.isErasedMethod) @@ -303,6 +309,8 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase if tree.symbol.isQuote then ctx.compilationUnit.needsStaging = true ctx.compilationUnit.needsQuotePickling = true + if tree.symbol.is(Inline) then + ctx.compilationUnit.needsInlining = true val tree1 @ TypeApply(fn, args) = normalizeTypeArgs(tree) args.foreach(checkInferredWellFormed) if (fn.symbol != defn.ChildAnnot.primaryConstructor) From 4cb4bbbda84f6192eb637759e6e7841a203a2c63 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 2 Feb 2021 13:54:24 +0100 Subject: [PATCH 43/56] Add quotes.isWhileTyping --- .../quoted/runtime/impl/QuotesImpl.scala | 4 ++++ library/src/scala/quoted/Quotes.scala | 13 ++++++++++++ tests/run-macros/is-in-typer/Macro_1.scala | 9 +++++++++ tests/run-macros/is-in-typer/Test_2.scala | 20 +++++++++++++++++++ 4 files changed, 46 insertions(+) create mode 100644 tests/run-macros/is-in-typer/Macro_1.scala create mode 100644 tests/run-macros/is-in-typer/Test_2.scala diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index 263fc51f8ba7..134f7e8507b8 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -76,6 +76,10 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler object reflect extends reflectModule: + object CompilationInfo extends CompilationInfoModule: + def isWhileTyping: Boolean = !ctx.isAfterTyper + end CompilationInfo + extension (expr: Expr[Any]) def asTerm: Term = val exprImpl = expr.asInstanceOf[ExprImpl] diff --git a/library/src/scala/quoted/Quotes.scala b/library/src/scala/quoted/Quotes.scala index 09a7d11bfd6a..31c840e49578 100644 --- a/library/src/scala/quoted/Quotes.scala +++ b/library/src/scala/quoted/Quotes.scala @@ -217,6 +217,19 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => */ trait reflectModule { self: reflect.type => + /** Module object of `type CompilationInfo` */ + val CompilationInfo: CompilationInfoModule + + /** Methods of the module object `val CompilationInfo` */ + trait CompilationInfoModule { this: CompilationInfo.type => + /** Are we expanding a `inline` macro while typing the program? + * + * This will be true when the macro is used in a transparent inline. + */ + def isWhileTyping: Boolean + } + + /** Returns the `Term` representation this expression */ extension (expr: Expr[Any]) def asTerm: Term diff --git a/tests/run-macros/is-in-typer/Macro_1.scala b/tests/run-macros/is-in-typer/Macro_1.scala new file mode 100644 index 000000000000..80157dec0845 --- /dev/null +++ b/tests/run-macros/is-in-typer/Macro_1.scala @@ -0,0 +1,9 @@ +import scala.quoted._ + +inline def isWhileTyping: Boolean = ${ whileTypeing } + +transparent inline def isWhileTypingTransparent: Boolean = ${ whileTypeing } + +private def whileTypeing(using Quotes): Expr[Boolean] = + import quotes.reflect._ + Expr(CompilationInfo.isWhileTyping) diff --git a/tests/run-macros/is-in-typer/Test_2.scala b/tests/run-macros/is-in-typer/Test_2.scala new file mode 100644 index 000000000000..5bd283c4aca4 --- /dev/null +++ b/tests/run-macros/is-in-typer/Test_2.scala @@ -0,0 +1,20 @@ + +@main def Test = + assert(!isWhileTyping) + assert(isWhileTypingTransparent) + assert(f1 == "afterTyper") + assert(f2 == "afterTyper") + assert(f3 == "inTyper") + assert(f4 == "inTyper") + +inline def f1 = + inline if isWhileTyping then "inTyper" else "afterTyper" + +inline def f2 = + inline if isWhileTypingTransparent /*delayed*/ then "inTyper" else "afterTyper" + +transparent inline def f3 = + inline if isWhileTyping /*forced*/ then "inTyper" else "afterTyper" + +transparent inline def f4 = + inline if isWhileTypingTransparent then "inTyper" else "afterTyper" From e33c6329d0f70912ab01960edb11a8cf02f5b2d8 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 2 Feb 2021 14:02:47 +0100 Subject: [PATCH 44/56] Fix needsInlining condition for inline val rhs check --- compiler/src/dotty/tools/dotc/transform/PostTyper.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 3ebaa15d71bc..0f7b3b0c918d 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -338,6 +338,8 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase ) } case tree: ValDef => + if tree.symbol.is(Inline, butNot = Param) then + ctx.compilationUnit.needsInlining = true val tree1 = cpy.ValDef(tree)(rhs = normalizeErasedRhs(tree.rhs, tree.symbol)) processValOrDefDef(super.transform(tree1)) case tree: DefDef => From df1ba15140ade88616ce154b92eeb824ba0f3b74 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 2 Feb 2021 15:41:49 +0100 Subject: [PATCH 45/56] Force inline scrutinees of inline matches --- compiler/src/dotty/tools/dotc/typer/Inliner.scala | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 82c0ad42917f..7e54e7440830 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -1300,6 +1300,15 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) { override def typedTypeApply(tree: untpd.TypeApply, pt: Type)(using Context): Tree = inlineIfIsNestedInlineCall(constToLiteral(betaReduce(super.typedTypeApply(tree, pt)))) + override def typedMatch(tree: untpd.Match, pt: Type)(using Context): Tree = + val tree1 = + if tree.isInline then + // TODO this might not be useful if we do not support #11291 + val sel1 = typedExpr(tree.selector)(using ctx.addMode(Mode.ForceInline)) + untpd.cpy.Match(tree)(sel1, tree.cases) + else tree + super.typedMatch(tree1, pt) + override def typedMatchFinish(tree: untpd.Match, sel: Tree, wideSelType: Type, cases: List[untpd.CaseDef], pt: Type)(using Context) = if (!tree.isInline || ctx.owner.isInlineMethod) // don't reduce match of nested inline method yet super.typedMatchFinish(tree, sel, wideSelType, cases, pt) From e3858e4d99e4336357583d9b6a131feddacd68da Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 3 Feb 2021 13:12:32 +0100 Subject: [PATCH 46/56] Re-enable izumi-reflect tests --- community-build/community-projects/izumi-reflect | 2 +- .../test/scala/dotty/communitybuild/CommunityBuildTest.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/community-build/community-projects/izumi-reflect b/community-build/community-projects/izumi-reflect index 9a332c9011bb..85a218963729 160000 --- a/community-build/community-projects/izumi-reflect +++ b/community-build/community-projects/izumi-reflect @@ -1 +1 @@ -Subproject commit 9a332c9011bb018a4b36f582f30e53966c6f5219 +Subproject commit 85a218963729d141ff66cda82fc5a258de2ae95b diff --git a/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala b/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala index d9662c4ebd65..0154369438a6 100644 --- a/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala +++ b/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala @@ -86,7 +86,7 @@ class CommunityBuildTestA extends CommunityBuildTest: @Test def fansi = projects.fansi.run() @Test def fastparse = projects.fastparse.run() @Test def geny = projects.geny.run() - @Ignore @Test def izumiReflect = projects.izumiReflect.run() + @Test def izumiReflect = projects.izumiReflect.run() @Test def oslib = projects.oslib.run() // @Test def oslibWatch = projects.oslibWatch.run() @Test def pprint = projects.pprint.run() From d335b0f0710de2c5bb9a9d840ae44c46e4ae8434 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 4 Feb 2021 10:51:14 +0100 Subject: [PATCH 47/56] Review --- .../src/dotty/tools/dotc/config/ScalaSettings.scala | 2 +- .../src/dotty/tools/dotc/transform/Inlining.scala | 2 +- .../src/dotty/tools/dotc/transform/PostTyper.scala | 4 ++-- docs/docs/reference/metaprogramming/inline.md | 12 +++++++----- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index ae860a0634ff..2abe3a750079 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -227,7 +227,7 @@ class ScalaSettings extends Settings.SettingGroup with CommonScalaSettings { val Yinstrument: Setting[Boolean] = BooleanSetting("-Yinstrument", "Add instrumentation code that counts allocations and closure creations.") val YinstrumentDefs: Setting[Boolean] = BooleanSetting("-Yinstrument-defs", "Add instrumentation code that counts method calls; needs -Yinstrument to be set, too.") - val YforceInlineWhileTyping: Setting[Boolean] = BooleanSetting("-Yforce-inline-while-typing", "") + val YforceInlineWhileTyping: Setting[Boolean] = BooleanSetting("-Yforce-inline-while-typing", "Make non-transparent inline methods inline when typing. Emulates the old inlining behavior of 3.0.0-M3.") /** Dottydoc specific settings that are not used in scaladoc */ val docSnapshot: Setting[Boolean] = BooleanSetting("-doc-snapshot", "Generate a documentation snapshot for the current Dotty version") diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala index ce2e8355237e..b70387ed3c55 100644 --- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala @@ -106,7 +106,7 @@ class Inlining extends MacroTransform { object Inlining { val name: String = "inlining" - /** Check that `vdef.rhs` can be right hand-side or argument to `inline` value or parameter. */ + /** Check that `vdef.rhs` can be right hand-side of an `inline` value definition. */ def checkInlineConformant(vdef: tpd.ValDef)(using Context): Unit = { val ValDef(_, tpt, rhs) = vdef if vdef.symbol.is(Inline, butNot = DeferredOrTermParamOrAccessor) && !ctx.erasedTypes && !Inliner.inInlineMethod then diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 0f7b3b0c918d..754f037fc65f 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -258,7 +258,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase override def transform(tree: Tree)(using Context): Tree = try tree match { case tree: Ident if !tree.isType => - if tree.symbol.is(Inline) then + if tree.symbol.is(Inline) && !Inliner.inInlineMethod then ctx.compilationUnit.needsInlining = true checkNoConstructorProxy(tree) tree.tpe match { @@ -276,7 +276,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase checkNoConstructorProxy(tree) transformSelect(tree, Nil) case tree: Apply => - if tree.symbol.is(Inline) then + if tree.symbol.is(Inline) && !Inliner.inInlineMethod then ctx.compilationUnit.needsInlining = true val methType = tree.fun.tpe.widen val app = diff --git a/docs/docs/reference/metaprogramming/inline.md b/docs/docs/reference/metaprogramming/inline.md index b798b952f80d..d7275df6f257 100644 --- a/docs/docs/reference/metaprogramming/inline.md +++ b/docs/docs/reference/metaprogramming/inline.md @@ -285,10 +285,11 @@ val one: 1 = zero + 1 ``` ### Transparent vs. non-transparent inline -As we already discussed, transparent inline methods will provide a more precise. -Technically this implies that transparent inline methods must be expanded while typing the program. Other inline methods are inlined later after the program is fully typed. +As we already discussed, transparent inline methods may influence type checking at call site. +Technically this implies that transparent inline methods must be expanded during type checking of the program. +Other inline methods are inlined later after the program is fully typed. -For example the following two functions will be typed the same way but will be inlined at different times. +For example, the following two functions will be typed the same way but will be inlined at different times. ```scala inline def f1: T = ... transparent inline def f2: T = (...): T @@ -297,7 +298,8 @@ transparent inline def f2: T = (...): T A noteworthy difference is the behavior of `transparent inline given`. If there is an error reported when inlining that definition, it will be considered as an implicit search mismatch and the search will continue. A `transparent inline given` can add a type ascription in its RHS (as in `f2` from the previous example) to avoid the precise type but keep the search behavior. -On the other hand `inline given` be taken as the implicit and then after typing is done the code is inlined and any error will be emitted as usual. +On the other hand, `inline given` be taken as the implicit and then inlined after typing. +Any error will be emitted as usual. ## Inline Conditionals @@ -328,7 +330,7 @@ below: | This location is in code that was inlined at ... ``` -In a transparent inline, an `inline if` will force the inlining of any inline definition in its condition. +In a transparent inline, an `inline if` will force the inlining of any inline definition in its condition during type checking. ## Inline Matches From dd24130ebc784d7462dc0229fa323502b70bd14c Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 4 Feb 2021 11:07:05 +0100 Subject: [PATCH 48/56] Update shapeless --- community-build/community-projects/shapeless | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/community-build/community-projects/shapeless b/community-build/community-projects/shapeless index 56bcb5246bca..bfa5dba99d64 160000 --- a/community-build/community-projects/shapeless +++ b/community-build/community-projects/shapeless @@ -1 +1 @@ -Subproject commit 56bcb5246bca8099d12fa54379ece6fb440657ec +Subproject commit bfa5dba99d6402336a1fbaf687f5cb29b3e78fe2 From f465f6ac976814b2296b377b389697293c82aefd Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 4 Feb 2021 13:29:52 +0100 Subject: [PATCH 49/56] Update community build --- community-build/community-projects/izumi-reflect | 2 +- community-build/community-projects/utest | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/community-build/community-projects/izumi-reflect b/community-build/community-projects/izumi-reflect index 85a218963729..54051d0bca92 160000 --- a/community-build/community-projects/izumi-reflect +++ b/community-build/community-projects/izumi-reflect @@ -1 +1 @@ -Subproject commit 85a218963729d141ff66cda82fc5a258de2ae95b +Subproject commit 54051d0bca921706ef0a3f9f63264f6f57d50ef0 diff --git a/community-build/community-projects/utest b/community-build/community-projects/utest index 64fc7b243980..cf30b7c40425 160000 --- a/community-build/community-projects/utest +++ b/community-build/community-projects/utest @@ -1 +1 @@ -Subproject commit 64fc7b243980e87fac573fc831552eea70abb2b3 +Subproject commit cf30b7c40425d4d3809eef462888e748bff0c809 From adee773d59d379822e13a13e043ec5c8324c474e Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 4 Feb 2021 15:28:22 +0100 Subject: [PATCH 50/56] Update utest --- community-build/community-projects/utest | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/community-build/community-projects/utest b/community-build/community-projects/utest index cf30b7c40425..7b255200129f 160000 --- a/community-build/community-projects/utest +++ b/community-build/community-projects/utest @@ -1 +1 @@ -Subproject commit cf30b7c40425d4d3809eef462888e748bff0c809 +Subproject commit 7b255200129fe084112eeb56c878220f216f3e36 From aee15557704fec9abc96428874f219c258c84712 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 4 Feb 2021 15:57:34 +0100 Subject: [PATCH 51/56] Add regression test --- community-build/community-projects/utest | 2 +- tests/pos-macros/classesInInlinedParam.scala | 27 ++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 tests/pos-macros/classesInInlinedParam.scala diff --git a/community-build/community-projects/utest b/community-build/community-projects/utest index 7b255200129f..f1ca7b12897f 160000 --- a/community-build/community-projects/utest +++ b/community-build/community-projects/utest @@ -1 +1 @@ -Subproject commit 7b255200129fe084112eeb56c878220f216f3e36 +Subproject commit f1ca7b12897f0fedccc757c17552ebbade72c6a2 diff --git a/tests/pos-macros/classesInInlinedParam.scala b/tests/pos-macros/classesInInlinedParam.scala new file mode 100644 index 000000000000..355d29cc8e29 --- /dev/null +++ b/tests/pos-macros/classesInInlinedParam.scala @@ -0,0 +1,27 @@ +inline def f(inline thunk: Any): Unit = thunk +transparent inline def g(inline thunk: Any): Unit = thunk + +def test: Unit = + f { + class C1(val i: Int) + val c = C1(1) + c.i + } + + f { + case class C2(i: Int) + val c = C2(2) + c.i + } + + g { + class C3(val i: Int) + val c = C3(3) + c.i + } + + g { + case class C4(i: Int) + val c = C4(4) + c.i + } \ No newline at end of file From 53d69cdeb54111ce5b23c6a70ccb9b87016496b9 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 5 Feb 2021 10:08:17 +0100 Subject: [PATCH 52/56] Refactor inlining condition logic --- .../dotty/tools/dotc/transform/Inlining.scala | 2 +- .../src/dotty/tools/dotc/typer/Inliner.scala | 57 +++++++++++-------- .../src/dotty/tools/dotc/typer/ReTyper.scala | 2 +- .../src/dotty/tools/dotc/typer/Typer.scala | 14 ++--- tests/pos-custom-args/erased/i7878.scala | 2 +- 5 files changed, 41 insertions(+), 36 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala index b70387ed3c55..f68e3b2346c5 100644 --- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala @@ -87,7 +87,7 @@ class Inlining extends MacroTransform { else super.transform(tree) case _: Typed | _: Block => super.transform(tree) - case _ if Inliner.isInlineable(tree) && !tree.tpe.widen.isInstanceOf[MethodOrPoly] && StagingContext.level == 0 => + case _ if Inliner.needsInlining(tree) => val tree1 = super.transform(tree) if tree1.tpe.isError then tree1 else Inliner.inlineCall(tree1) diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 7e54e7440830..01462c4ed48b 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -52,16 +52,29 @@ object Inliner { def inInlineMethod(using Context): Boolean = ctx.owner.ownersIterator.exists(_.isInlineMethod) - /** Should call to method `meth` be inlined in this context? */ + /** Can a call to method `meth` be inlined? */ def isInlineable(meth: Symbol)(using Context): Boolean = meth.is(Inline) && meth.hasAnnotation(defn.BodyAnnot) && !inInlineMethod /** Should call be inlined in this context? */ - def isInlineable(tree: Tree)(using Context): Boolean = tree match { - case Block(_, expr) => isInlineable(expr) - case _ => isInlineable(tree.symbol) && !tree.tpe.isInstanceOf[MethodOrPoly] + def needsInlining(tree: Tree)(using Context): Boolean = tree match { + case Block(_, expr) => needsInlining(expr) + case _ => + isInlineable(tree.symbol) + && !tree.tpe.widenTermRefExpr.isInstanceOf[MethodOrPoly] + && StagingContext.level == 0 + && ( + ctx.phase == Phases.inliningPhase + || (ctx.phase == Phases.typerPhase && needsTransparentInlining(tree)) + ) + && !ctx.typer.hasInliningErrors } + private def needsTransparentInlining(tree: Tree)(using Context): Boolean = + tree.symbol.is(Transparent) + || ctx.mode.is(Mode.ForceInline) + || ctx.settings.YforceInlineWhileTyping.value + /** Try to inline a call to an inline method. Fail with error if the maximal * inline depth is exceeded. * @@ -283,6 +296,7 @@ object Inliner { assert(tree.symbol == defn.CompiletimeTesting_typeChecks || tree.symbol == defn.CompiletimeTesting_typeCheckErrors) def stripTyped(t: Tree): Tree = t match { case Typed(t2, _) => stripTyped(t2) + case Block(Nil, t2) => stripTyped(t2) case Inlined(_, Nil, t2) => stripTyped(t2) case _ => t } @@ -290,7 +304,7 @@ object Inliner { val Apply(_, codeArg :: Nil) = tree val codeArg1 = stripTyped(codeArg.underlying) val underlyingCodeArg = - if Inliner.isInlineable(codeArg1) then stripTyped(Inliner.inlineCall(codeArg1)) + if Inliner.isInlineable(codeArg1.symbol) then stripTyped(Inliner.inlineCall(codeArg1)) else codeArg1 ConstFold(underlyingCodeArg).tpe.widenTermRefExpr match { @@ -1240,9 +1254,14 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) { super.ensureAccessible(tpe, superAccess, pos) } + override def typed(tree: untpd.Tree, pt: Type = WildcardType)(using Context): Tree = + val tree1 = super.typed(tree, pt) + if Inliner.needsInlining(tree1) + then Inliner.inlineCall(tree1) + else tree1 + override def typedIdent(tree: untpd.Ident, pt: Type)(using Context): Tree = - val tree1 = tryInlineArg(tree.asInstanceOf[tpd.Tree]) `orElse` super.typedIdent(tree, pt) - inlineIfIsNestedInlineCall(tree1) + tryInlineArg(tree.asInstanceOf[tpd.Tree]) `orElse` super.typedIdent(tree, pt) override def typedSelect(tree: untpd.Select, pt: Type)(using Context): Tree = { assert(tree.hasType, tree) @@ -1254,7 +1273,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) { else val res = resMaybeReduced ensureAccessible(res.tpe, tree.qualifier.isInstanceOf[untpd.Super], tree.srcPos) - inlineIfIsNestedInlineCall(res) + res } override def typedIf(tree: untpd.If, pt: Type)(using Context): Tree = @@ -1287,18 +1306,18 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) { val res = constToLiteral(betaReduce(super.typedApply(tree, pt))) match { case res: Apply if res.symbol == defn.QuotedRuntime_exprSplice && level == 0 - && !suppressInline => + && !hasInliningErrors => val expanded = expandMacro(res.args.head, tree.span) typedExpr(expanded) // Inline calls and constant fold code generated by the macro case res => - inlineIfIsNestedInlineCall(res) + res } if res.symbol == defn.QuotedRuntime_exprQuote then ctx.compilationUnit.needsQuotePickling = true res override def typedTypeApply(tree: untpd.TypeApply, pt: Type)(using Context): Tree = - inlineIfIsNestedInlineCall(constToLiteral(betaReduce(super.typedTypeApply(tree, pt)))) + constToLiteral(betaReduce(super.typedTypeApply(tree, pt))) override def typedMatch(tree: untpd.Match, pt: Type)(using Context): Tree = val tree1 = @@ -1354,19 +1373,9 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) { override def newLikeThis: Typer = new InlineTyper(initialErrorCount) - /** Suppress further inlining if this inline typer has already issued errors */ - override def suppressInline(using Context) = - ctx.reporter.errorCount > initialErrorCount || super.suppressInline - - private def inlineIfIsNestedInlineCall(tree: Tree)(using Context): Tree = - if - !suppressInline && - !tree.tpe.widenTermRefExpr.isInstanceOf[MethodOrPoly] && - Inliner.isInlineable(tree) && - StagingContext.level == 0 && - (ctx.isAfterTyper || tree.symbol.is(Transparent) || ctx.mode.is(Mode.ForceInline) || ctx.settings.YforceInlineWhileTyping.value) - then Inliner.inlineCall(tree) - else tree + /** True if this inline typer has already issued errors */ + override def hasInliningErrors(using Context) = ctx.reporter.errorCount > initialErrorCount + } /** Drop any side-effect-free bindings that are unused in expansion or other reachable bindings. diff --git a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala index 0e91ce964620..8ff0dfc8c61b 100644 --- a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala +++ b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala @@ -118,7 +118,7 @@ class ReTyper extends Typer with ReChecking { try super.typedUnadapted(tree, pt, locked) catch { case NonFatal(ex) => - if ctx.isAfterTyper && ctx.phase != Phases.inliningPhase then + if ctx.phase != Phases.typerPhase && ctx.phase != Phases.inliningPhase then println(i"exception while typing $tree of class ${tree.getClass} # ${tree.uniqueId}") throw ex } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 894c5e6dde0c..65ccef441bbe 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -865,9 +865,9 @@ class Typer extends Namer */ val arg1 = pt match { case AppliedType(a, typ :: Nil) if ctx.isJava && a.isRef(defn.ArrayClass) => - tryAlternatively { typed(tree.arg, pt) } { + tryAlternatively { typed(tree.arg, pt) } { val elemTp = untpd.TypedSplice(TypeTree(typ)) - typed(untpd.JavaSeqLiteral(tree.arg :: Nil, elemTp), pt) + typed(untpd.JavaSeqLiteral(tree.arg :: Nil, elemTp), pt) } case _ => typed(tree.arg, pt) } @@ -3406,11 +3406,7 @@ class Typer extends Namer val meth = methPart(tree).symbol if meth.isAllOf(DeferredInline) && !Inliner.inInlineMethod then errorTree(tree, i"Deferred inline ${meth.showLocated} cannot be invoked") - else if - Inliner.isInlineable(tree) && - !suppressInline && - StagingContext.level == 0 && - (tree.symbol.is(Transparent) || ctx.settings.YforceInlineWhileTyping.value) + else if Inliner.needsInlining(tree) then tree.tpe <:< wildApprox(pt) val errorCount = ctx.reporter.errorCount @@ -3734,8 +3730,8 @@ class Typer extends Namer } } - // Overridden in InlineTyper - def suppressInline(using Context): Boolean = ctx.isAfterTyper && ctx.phase != Phases.inliningPhase + /** True if this inline typer has already issued errors */ + def hasInliningErrors(using Context): Boolean = false /** Does the "contextuality" of the method type `methType` match the one of the prototype `pt`? * This is the case if diff --git a/tests/pos-custom-args/erased/i7878.scala b/tests/pos-custom-args/erased/i7878.scala index d47bb32f1a65..7a27cdad4c14 100644 --- a/tests/pos-custom-args/erased/i7878.scala +++ b/tests/pos-custom-args/erased/i7878.scala @@ -2,7 +2,7 @@ object Boom { import scala.compiletime._ trait Fail[A <: Int, B <: Int] - erased inline given fail[X <: Int, Y <: Int]: Fail[X, Y] = { + erased transparent inline given fail[X <: Int, Y <: Int]: Fail[X, Y] = { scala.compiletime.summonFrom { case t: Fail[X, y] if constValue[y] < constValue[Y] => ??? } From 891a96a9bbc47ff428ac11fadfe21e2ce7cb4df8 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 8 Feb 2021 09:35:30 +0100 Subject: [PATCH 53/56] Check if needs inline only for RefTree and GenericApply trees --- .../src/dotty/tools/dotc/typer/Inliner.scala | 18 ++++++++---------- .../src/dotty/tools/dotc/typer/Typer.scala | 3 +-- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 01462c4ed48b..d8650cd49145 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -1254,14 +1254,8 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) { super.ensureAccessible(tpe, superAccess, pos) } - override def typed(tree: untpd.Tree, pt: Type = WildcardType)(using Context): Tree = - val tree1 = super.typed(tree, pt) - if Inliner.needsInlining(tree1) - then Inliner.inlineCall(tree1) - else tree1 - override def typedIdent(tree: untpd.Ident, pt: Type)(using Context): Tree = - tryInlineArg(tree.asInstanceOf[tpd.Tree]) `orElse` super.typedIdent(tree, pt) + inlineIfNeeded(tryInlineArg(tree.asInstanceOf[tpd.Tree]) `orElse` super.typedIdent(tree, pt)) override def typedSelect(tree: untpd.Select, pt: Type)(using Context): Tree = { assert(tree.hasType, tree) @@ -1273,7 +1267,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) { else val res = resMaybeReduced ensureAccessible(res.tpe, tree.qualifier.isInstanceOf[untpd.Super], tree.srcPos) - res + inlineIfNeeded(res) } override def typedIf(tree: untpd.If, pt: Type)(using Context): Tree = @@ -1310,14 +1304,14 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) { val expanded = expandMacro(res.args.head, tree.span) typedExpr(expanded) // Inline calls and constant fold code generated by the macro case res => - res + inlineIfNeeded(res) } if res.symbol == defn.QuotedRuntime_exprQuote then ctx.compilationUnit.needsQuotePickling = true res override def typedTypeApply(tree: untpd.TypeApply, pt: Type)(using Context): Tree = - constToLiteral(betaReduce(super.typedTypeApply(tree, pt))) + inlineIfNeeded(constToLiteral(betaReduce(super.typedTypeApply(tree, pt)))) override def typedMatch(tree: untpd.Match, pt: Type)(using Context): Tree = val tree1 = @@ -1376,6 +1370,10 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) { /** True if this inline typer has already issued errors */ override def hasInliningErrors(using Context) = ctx.reporter.errorCount > initialErrorCount + private def inlineIfNeeded(tree: Tree)(using Context): Tree = + if Inliner.needsInlining(tree) then Inliner.inlineCall(tree) + else tree + } /** Drop any side-effect-free bindings that are unused in expansion or other reachable bindings. diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 65ccef441bbe..3063d6ea17af 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -3406,8 +3406,7 @@ class Typer extends Namer val meth = methPart(tree).symbol if meth.isAllOf(DeferredInline) && !Inliner.inInlineMethod then errorTree(tree, i"Deferred inline ${meth.showLocated} cannot be invoked") - else if Inliner.needsInlining(tree) - then + else if Inliner.needsInlining(tree) then tree.tpe <:< wildApprox(pt) val errorCount = ctx.reporter.errorCount val inlined = Inliner.inlineCall(tree) From dea04b3d6284faeb3f9bc43a1246413b1ad9a66b Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 8 Feb 2021 09:46:35 +0100 Subject: [PATCH 54/56] Only force inline conditions of inline if --- .../src/dotty/tools/dotc/transform/YCheckPositions.scala | 9 +++++---- compiler/src/dotty/tools/dotc/typer/Inliner.scala | 3 ++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/YCheckPositions.scala b/compiler/src/dotty/tools/dotc/transform/YCheckPositions.scala index 233f20df9f4e..44655e3bead3 100644 --- a/compiler/src/dotty/tools/dotc/transform/YCheckPositions.scala +++ b/compiler/src/dotty/tools/dotc/transform/YCheckPositions.scala @@ -28,10 +28,11 @@ class YCheckPositions extends Phase { // Check current context is correct assert(ctx.source == sources.head) if (!tree.isEmpty && !tree.isInstanceOf[untpd.TypedSplice] && !tree.isInstanceOf[Inlined] && ctx.typerState.isGlobalCommittable) - if (!tree.isType) { // TODO also check types, currently we do not add Inlined(EmptyTree, _, _) for types. We should. + if !tree.isType // TODO also check types, currently we do not add Inlined(EmptyTree, _, _) for types. We should. + && !tree.symbol.is(InlineProxy) // TODO check inline proxies (see tests/tun/lst) + then val currentSource = sources.head - assert(tree.source == currentSource, i"wrong source set for $tree # ${tree.uniqueId} of ${tree.getClass}, set to ${tree.source} but context had $currentSource") - } + assert(tree.source == currentSource, i"wrong source set for $tree # ${tree.uniqueId} of ${tree.getClass}, set to ${tree.source} but context had $currentSource\n ${tree.symbol.flagsString}") // Recursivlely check children while keeping track of current source tree match { @@ -42,7 +43,7 @@ class YCheckPositions extends Phase { traverse(expansion)(using inlineContext(EmptyTree).withSource(sources.head)) sources = old case Inlined(call, bindings, expansion) => - bindings.foreach(traverse(_)) + // bindings.foreach(traverse(_)) // TODO check inline proxies (see tests/tun/lst) 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)(using inlineContext(call).withSource(sources.head)) diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index d8650cd49145..875439675b0d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -1271,7 +1271,8 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) { } override def typedIf(tree: untpd.If, pt: Type)(using Context): Tree = - typed(tree.cond, defn.BooleanType)(using ctx.addMode(Mode.ForceInline)) match { + val condCtx = if tree.isInline then ctx.addMode(Mode.ForceInline) else ctx + typed(tree.cond, defn.BooleanType)(using condCtx) match { case cond1 @ ConstantValue(b: Boolean) => val selected0 = if (b) tree.thenp else tree.elsep val selected = if (selected0.isEmpty) tpd.Literal(Constant(())) else typed(selected0, pt) From ec71a6172c6de4b3ef94efef432e29160123c678 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 8 Feb 2021 09:49:31 +0100 Subject: [PATCH 55/56] Remove redundant check Those are checked by the Ident or Select of their function parts --- compiler/src/dotty/tools/dotc/transform/PostTyper.scala | 2 -- 1 file changed, 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 754f037fc65f..357d8a955101 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -276,8 +276,6 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase checkNoConstructorProxy(tree) transformSelect(tree, Nil) case tree: Apply => - if tree.symbol.is(Inline) && !Inliner.inInlineMethod then - ctx.compilationUnit.needsInlining = true val methType = tree.fun.tpe.widen val app = if (methType.isErasedMethod) From bdf24824be9e0f1360dc26521e3cacf95367ef1d Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 8 Feb 2021 10:35:56 +0100 Subject: [PATCH 56/56] Check rhs of inline val after Inlining phase To avoid running the phase --- compiler/src/dotty/tools/dotc/Compiler.scala | 1 + .../tools/dotc/transform/InlineVals.scala | 43 +++++++++++++++++++ .../dotty/tools/dotc/transform/Inlining.scala | 25 +---------- .../tools/dotc/transform/PostTyper.scala | 2 - tests/neg/inlinevals-2.scala | 4 -- tests/neg/inlinevals-4.scala | 13 ++++++ 6 files changed, 58 insertions(+), 30 deletions(-) create mode 100644 compiler/src/dotty/tools/dotc/transform/InlineVals.scala create mode 100644 tests/neg/inlinevals-4.scala diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index f20db4d9560b..e1fa879df834 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -62,6 +62,7 @@ class Compiler { new CookComments, // Cook the comments: expand variables, doc, etc. new CheckStatic, // Check restrictions that apply to @static members new BetaReduce, // Reduce closure applications + new InlineVals, // Check right hand-sides of an `inline val`s new ExpandSAMs, // Expand single abstract method closures to anonymous classes new init.Checker) :: // Check initialization of objects List(new ElimRepeated, // Rewrite vararg parameters and arguments diff --git a/compiler/src/dotty/tools/dotc/transform/InlineVals.scala b/compiler/src/dotty/tools/dotc/transform/InlineVals.scala new file mode 100644 index 000000000000..0a7d6a9e7a07 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/InlineVals.scala @@ -0,0 +1,43 @@ +package dotty.tools +package dotc +package transform + +import dotty.tools.dotc.core.Contexts._ +import dotty.tools.dotc.core.Decorators._ +import dotty.tools.dotc.core.Flags._ +import dotty.tools.dotc.core.Types._ +import dotty.tools.dotc.transform.MegaPhase.MiniPhase +import dotty.tools.dotc.typer.Inliner + +/** Check that `tree.rhs` can be right hand-side of an `inline` value definition. */ +class InlineVals extends MiniPhase: + import ast.tpd._ + + def phaseName: String = "inlineVals" + + override def checkPostCondition(tree: Tree)(using Context): Unit = + if !ctx.erasedTypes then + tree match + case tree: ValDef => checkInlineConformant(tree) + case _ => + + override def transformValDef(tree: ValDef)(using Context): Tree = + checkInlineConformant(tree) + tree + + /** Check that `tree.rhs` can be right hand-side of an `inline` value definition. */ + private def checkInlineConformant(tree: ValDef)(using Context): Unit = { + if tree.symbol.is(Inline, butNot = DeferredOrTermParamOrAccessor) + && !Inliner.inInlineMethod + then + val rhs = tree.rhs + val tpt = tree.tpt + tpt.tpe.widenTermRefExpr.dealias.normalized match + case tp: ConstantType => + if !isPureExpr(rhs) then + val details = if enclosingInlineds.isEmpty then "" else em"but was: $rhs" + report.error(s"inline value must be pure$details", rhs.srcPos) + case _ => + val pos = if tpt.span.isZeroExtent then rhs.srcPos else tpt.srcPos + report.error(em"inline value must have a literal constant type", pos) + } \ No newline at end of file diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala index f68e3b2346c5..5db2afdacd8d 100644 --- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala @@ -61,8 +61,6 @@ class Inlining extends MacroTransform { traverseChildren(tree)(using StagingContext.spliceContext) case tree: RefTree if !Inliner.inInlineMethod && StagingContext.level == 0 => assert(!tree.symbol.isInlineMethod, tree.show) - case tree: ValDef => - checkInlineConformant(tree) case _ => traverseChildren(tree) }.traverse(tree) @@ -77,11 +75,6 @@ class Inlining extends MacroTransform { private class InliningTreeMap extends TreeMapWithImplicits { override def transform(tree: Tree)(using Context): Tree = { tree match - case tree: ValDef => - super.transform(tree) match - case tree: ValDef => - checkInlineConformant(tree) - tree case tree: DefTree => if tree.symbol.is(Inline) then tree else super.transform(tree) @@ -103,21 +96,5 @@ class Inlining extends MacroTransform { } } -object Inlining { +object Inlining: val name: String = "inlining" - - /** Check that `vdef.rhs` can be right hand-side of an `inline` value definition. */ - def checkInlineConformant(vdef: tpd.ValDef)(using Context): Unit = { - val ValDef(_, tpt, rhs) = vdef - if vdef.symbol.is(Inline, butNot = DeferredOrTermParamOrAccessor) && !ctx.erasedTypes && !Inliner.inInlineMethod then - val rhs = vdef.rhs - vdef.tpt.tpe.widenTermRefExpr.dealias.normalized match - case tp: ConstantType => - if !tpd.isPureExpr(rhs) then - val details = if tpd.enclosingInlineds.isEmpty then "" else em"but was: $rhs" - report.error(s"inline value must be pure$details", rhs.srcPos) - case _ => - val pos = if vdef.tpt.span.isZeroExtent then rhs.srcPos else vdef.tpt.srcPos - report.error(em"inline value must have a literal constant type", pos) - } -} diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 357d8a955101..bc69b18e541b 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -336,8 +336,6 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase ) } case tree: ValDef => - if tree.symbol.is(Inline, butNot = Param) then - ctx.compilationUnit.needsInlining = true val tree1 = cpy.ValDef(tree)(rhs = normalizeErasedRhs(tree.rhs, tree.symbol)) processValOrDefDef(super.transform(tree1)) case tree: DefDef => diff --git a/tests/neg/inlinevals-2.scala b/tests/neg/inlinevals-2.scala index 1de454d46eae..9a3c6829c456 100644 --- a/tests/neg/inlinevals-2.scala +++ b/tests/neg/inlinevals-2.scala @@ -6,11 +6,7 @@ object Test { inline val N = 10 def X = 20 - inline val M = X // error: rhs must be constant expression - power(2.0, N) // ok, since it's a by-name parameter power(2.0, X) // error: cannot reduce inline if - inline val xs = List(1, 2, 3) // error: must be a constant expression - } diff --git a/tests/neg/inlinevals-4.scala b/tests/neg/inlinevals-4.scala new file mode 100644 index 000000000000..b4d4bfdfb579 --- /dev/null +++ b/tests/neg/inlinevals-4.scala @@ -0,0 +1,13 @@ +object Test { + + inline def power(x: Double, inline n: Int): Double = // ok + inline if n == 0 then ??? else ??? + + inline val N = 10 + def X = 20 + + inline val M = X // error: rhs must be constant expression + + inline val xs = List(1, 2, 3) // error: must be a constant expression + +}