Skip to content

Contextualize quote and splice internals #6881

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 30, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion community-build/community-projects/scalatest
Submodule scalatest updated 36 files
+7 −3 project/GenResources.scala
+1 −1 project/scalatest.scala
+4 −2 scalactic-test/src/test/scala/org/scalactic/ConversionCheckedTripleEqualsExplicitlySpec.scala
+1 −1 scalactic.dotty/src/main/scala/org/scalactic/source/Position.scala
+6 −6 scalatest.dotty/src/main/scala/org/scalatest/Assertions.scala
+1 −1 scalatest/src/main/scala/org/scalatest/AsyncOutcome.scala
+2 −2 scalatest/src/main/scala/org/scalatest/AsyncTestRegistration.scala
+2 −2 scalatest/src/main/scala/org/scalatest/DistributedSuiteSorter.scala
+3 −3 scalatest/src/main/scala/org/scalatest/DistributedTestSorter.scala
+2 −2 scalatest/src/main/scala/org/scalatest/Fact.scala
+1 −1 scalatest/src/main/scala/org/scalatest/JavaClassesWrappers.scala
+1 −1 scalatest/src/main/scala/org/scalatest/ParallelTestExecution.scala
+1 −1 scalatest/src/main/scala/org/scalatest/RecoverMethods.scala
+1 −1 scalatest/src/main/scala/org/scalatest/Reporter.scala
+1 −1 scalatest/src/main/scala/org/scalatest/ResourcefulReporter.scala
+9 −9 scalatest/src/main/scala/org/scalatest/Shell.scala
+2 −2 scalatest/src/main/scala/org/scalatest/Status.scala
+1 −1 scalatest/src/main/scala/org/scalatest/Stopper.scala
+1 −1 scalatest/src/main/scala/org/scalatest/Suite.scala
+2 −2 scalatest/src/main/scala/org/scalatest/TestRegistration.scala
+1 −1 scalatest/src/main/scala/org/scalatest/concurrent/Conductors.scala
+3 −3 scalatest/src/main/scala/org/scalatest/exceptions/StackDepthException.scala
+2 −2 scalatest/src/main/scala/org/scalatest/fixture/AsyncTestRegistration.scala
+2 −2 scalatest/src/main/scala/org/scalatest/fixture/TestRegistration.scala
+1 −1 scalatest/src/main/scala/org/scalatest/prop/CommonGenerators.scala
+2 −2 scalatest/src/main/scala/org/scalatest/prop/Randomizer.scala
+2 −2 scalatest/src/main/scala/org/scalatest/time/Span.scala
+1 −1 scalatest/src/main/scala/org/scalatest/tools/Durations.scala
+1 −1 scalatest/src/main/scala/org/scalatest/tools/Framework.scala
+4 −4 scalatest/src/main/scala/org/scalatest/tools/Runner.scala
+1 −1 scalatest/src/main/scala/org/scalatest/tools/RunnerJFrame.scala
+1 −1 scalatest/src/main/scala/org/scalatest/tools/ScalaTestFramework.scala
+1 −1 scalatest/src/main/scala/org/scalatest/tools/TestSortingReporter.scala
+1 −1 scalatest/src/main/scala/org/scalatest/tools/XmlReporter.scala
+1 −1 scalatest/src/main/scala/org/scalatest/words/ResultOfStringPassedToVerb.scala
+1 −1 scalatest/src/main/scala/org/scalatest/words/ResultOfTaggedAsInvocation.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import dotty.tools.dotc.core.Types._
import dotty.tools.dotc.core.tasty.TreePickler.Hole
import dotty.tools.dotc.core.tasty.{PositionPickler, TastyPickler, TastyPrinter, TastyString}
import dotty.tools.dotc.core.tasty.TreeUnpickler.UnpickleMode
import dotty.tools.dotc.tastyreflect.ReflectionImpl

import scala.internal.quoted._
import scala.reflect.ClassTag
Expand Down Expand Up @@ -47,7 +48,7 @@ object PickledQuotes {
forceAndCleanArtefacts.transform(unpickled)
case expr: TastyTreeExpr[Tree] @unchecked => healOwner(expr.tree)
case expr: FunctionAppliedTo[_] =>
functionAppliedTo(quotedExprToTree(expr.f), expr.args.map(arg => quotedExprToTree(arg)).toList)
functionAppliedTo(quotedExprToTree(expr.f), expr.args.map(arg => quotedExprToTree(arg(new scala.quoted.QuoteContext(ReflectionImpl(ctx))))).toList)
}

/** Transform the expression into its fully spliced TypeTree */
Expand Down
5 changes: 3 additions & 2 deletions compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1273,14 +1273,15 @@ class TreeUnpickler(reader: TastyReader,
val args = until(end)(readTerm())
val splice = splices(idx)
def wrap(arg: Tree) =
if (arg.isTerm) new TastyTreeExpr(arg)
if (arg.isTerm) given (qctx: scala.quoted.QuoteContext) => new TastyTreeExpr(arg)
else new TreeType(arg)
val reifiedArgs = args.map(wrap)
val filled = if (isType) {
val quotedType = splice.asInstanceOf[Seq[Any] => quoted.Type[_]](reifiedArgs)
PickledQuotes.quotedTypeToTree(quotedType)
} else {
val quotedExpr = splice.asInstanceOf[Seq[Any] => quoted.Expr[_]](reifiedArgs)
val splice1 = splice.asInstanceOf[Seq[Any] => given scala.quoted.QuoteContext => quoted.Expr[_]]
val quotedExpr = splice1(reifiedArgs) given new scala.quoted.QuoteContext(tastyreflect.ReflectionImpl(ctx))
PickledQuotes.quotedExprToTree(quotedExpr)
}
// We need to make sure a hole is created with the source file of the surrounding context, even if
Expand Down
7 changes: 5 additions & 2 deletions compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,19 @@ import dotty.tools.dotc.core.Decorators._
import dotty.tools.dotc.tastyreflect.FromSymbol.{definitionFromSym, packageDefFromSym}
import dotty.tools.dotc.parsing.Parsers.Parser
import dotty.tools.dotc.typer.Implicits.{AmbiguousImplicits, DivergingImplicit, NoMatchingImplicits, SearchFailure, SearchFailureType}
import dotty.tools.dotc.util.SourceFile
import dotty.tools.dotc.util.{SourceFile, SourcePosition, Spans}

import scala.tasty.reflect.Kernel

class KernelImpl(val rootContext: core.Contexts.Context, val rootPosition: util.SourcePosition) extends Kernel {
class KernelImpl(val rootContext: core.Contexts.Context) extends Kernel {

private implicit def ctx: core.Contexts.Context = rootContext

def settings: Settings = rootContext.settings

def rootPosition: util.SourcePosition =
tastyreflect.MacroExpansion.position.getOrElse(SourcePosition(rootContext.source, Spans.NoSpan))

//
// CONTEXT
//
Expand Down
18 changes: 18 additions & 0 deletions compiler/src/dotty/tools/dotc/tastyreflect/MacroExpansion.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package dotty.tools.dotc.tastyreflect

import dotty.tools.dotc.ast.tpd
import dotty.tools.dotc.core._
import dotty.tools.dotc.core.Contexts._
import dotty.tools.dotc.util.{Property, SourcePosition, Spans}

object MacroExpansion {

private val MacroExpansionPosition = new Property.Key[SourcePosition]

def position(implicit ctx: Context): Option[SourcePosition] =
ctx.property(MacroExpansionPosition)

def context(inlinedFrom: tpd.Tree)(implicit ctx: Context): Context =
ctx.fresh.setProperty(MacroExpansionPosition, SourcePosition(inlinedFrom.source, inlinedFrom.span)).withSource(inlinedFrom.source)

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,17 @@ package dotty.tools.dotc.tastyreflect

import dotty.tools.dotc.ast.tpd
import dotty.tools.dotc.core._
import dotty.tools.dotc.util.{SourcePosition, Spans}
import dotty.tools.dotc.core.Contexts._

import scala.quoted.show.SyntaxHighlight

object ReflectionImpl {

def apply(rootContext: Contexts.Context): scala.tasty.Reflection =
apply(rootContext, SourcePosition(rootContext.source, Spans.NoSpan))

def apply(rootContext: Contexts.Context, rootPosition: SourcePosition): scala.tasty.Reflection =
new scala.tasty.Reflection(new KernelImpl(rootContext, rootPosition))
new scala.tasty.Reflection(new KernelImpl(rootContext))

def showTree(tree: tpd.Tree)(implicit ctx: Contexts.Context): String = {
val refl = new scala.tasty.Reflection(new KernelImpl(ctx, tree.sourcePos))
val refl = new scala.tasty.Reflection(new KernelImpl(MacroExpansion.context(tree)))
val reflCtx = ctx.asInstanceOf[refl.Context]
val reflTree = tree.asInstanceOf[refl.Tree]
val syntaxHighlight =
Expand Down
51 changes: 27 additions & 24 deletions compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,6 @@ class ReifyQuotes extends MacroTransform {
*/
override protected def transformQuotation(body: Tree, quote: Tree)(implicit ctx: Context): Tree = {
val isType = quote.symbol eq defn.InternalQuoted_typeQuote
assert(!(body.symbol.isSplice && (body.isInstanceOf[GenericApply[_]] || body.isInstanceOf[Select])))
if (level > 0) {
val body1 = nested(isQuote = true).transform(body)(quoteContext)
super.transformQuotation(body1, quote)
Expand Down Expand Up @@ -209,35 +208,37 @@ class ReifyQuotes extends MacroTransform {
qctx
}

def liftedValue[T](value: T, name: TermName, qctx: Tree) =
ref(defn.LiftableModule).select(name).select("toExpr".toTermName).appliedTo(Literal(Constant(value))).select(nme.apply).appliedTo(qctx)

def pickleAsValue[T](value: T) = value match {
case null => ref(defn.QuotedExprModule).select("nullExpr".toTermName).appliedTo(qctx)
case _: Unit => ref(defn.QuotedExprModule).select("unitExpr".toTermName).appliedTo(qctx)
case _: Boolean => liftedValue(value, "Liftable_Boolean_delegate".toTermName, qctx)
case _: Byte => liftedValue(value, "Liftable_Byte_delegate".toTermName, qctx)
case _: Short => liftedValue(value, "Liftable_Short_delegate".toTermName, qctx)
case _: Int => liftedValue(value, "Liftable_Int_delegate".toTermName, qctx)
case _: Long => liftedValue(value, "Liftable_Long_delegate".toTermName, qctx)
case _: Float => liftedValue(value, "Liftable_Float_delegate".toTermName, qctx)
case _: Double => liftedValue(value, "Liftable_Double_delegate".toTermName, qctx)
case _: Char => liftedValue(value, "Liftable_Char_delegate".toTermName, qctx)
case _: String => liftedValue(value, "Liftable_String_delegate".toTermName, qctx)
def liftedValue[T](value: T, name: TermName) =
ref(defn.LiftableModule).select(name).select("toExpr".toTermName).appliedTo(Literal(Constant(value)))

def pickleAsValue[T](value: T) = {
value match {
case null => ref(defn.QuotedExprModule).select("nullExpr".toTermName)
case _: Unit => ref(defn.QuotedExprModule).select("unitExpr".toTermName)
case _: Boolean => liftedValue(value, "Liftable_Boolean_delegate".toTermName)
case _: Byte => liftedValue(value, "Liftable_Byte_delegate".toTermName)
case _: Short => liftedValue(value, "Liftable_Short_delegate".toTermName)
case _: Int => liftedValue(value, "Liftable_Int_delegate".toTermName)
case _: Long => liftedValue(value, "Liftable_Long_delegate".toTermName)
case _: Float => liftedValue(value, "Liftable_Float_delegate".toTermName)
case _: Double => liftedValue(value, "Liftable_Double_delegate".toTermName)
case _: Char => liftedValue(value, "Liftable_Char_delegate".toTermName)
case _: String => liftedValue(value, "Liftable_String_delegate".toTermName)
}
}

def pickleAsTasty() = {
val meth =
if (isType) ref(defn.Unpickler_unpickleType).appliedToType(originalTp)
else ref(defn.Unpickler_unpickleExpr).appliedToType(originalTp.widen)
def wildcardQuotedType = defn.QuotedTypeClass.typeRef.appliedTo(WildcardType)
val spliceResType =
if (isType) wildcardQuotedType
else defn.QuotedExprClass.typeRef.appliedTo(defn.AnyType) | wildcardQuotedType
meth.appliedTo(
liftList(PickledQuotes.pickleQuote(body).map(x => Literal(Constant(x))), defn.StringType),
liftList(splices, defn.FunctionType(1).appliedTo(defn.SeqType.appliedTo(defn.AnyType), spliceResType)))
if (isType) defn.QuotedTypeClass.typeRef.appliedTo(WildcardType)
else defn.FunctionType(1, isContextual = true).appliedTo(defn.QuoteContextClass.typeRef, defn.QuotedExprClass.typeRef.appliedTo(defn.AnyType)) | defn.QuotedTypeClass.typeRef.appliedTo(WildcardType)
val pickledQuoteStrings = liftList(PickledQuotes.pickleQuote(body).map(x => Literal(Constant(x))), defn.StringType)
val splicesList = liftList(splices, defn.FunctionType(1).appliedTo(defn.SeqType.appliedTo(defn.AnyType), spliceResType))
meth.appliedTo(pickledQuoteStrings, splicesList)
}

if (splices.nonEmpty) pickleAsTasty()
else if (isType) {
def tag(tagName: String) = ref(defn.QuotedTypeModule).select(tagName.toTermName).appliedTo(qctx)
Expand Down Expand Up @@ -316,7 +317,7 @@ class ReifyQuotes extends MacroTransform {
assert(tpw.isInstanceOf[ValueType])
val argTpe =
if (tree.isType) defn.QuotedTypeClass.typeRef.appliedTo(tpw)
else defn.QuotedExprClass.typeRef.appliedTo(tpw)
else defn.FunctionType(1, isContextual = true).appliedTo(defn.QuoteContextClass.typeRef, defn.QuotedExprClass.typeRef.appliedTo(tpw))
val selectArg = arg.select(nme.apply).appliedTo(Literal(Constant(i))).cast(argTpe)
val capturedArg = SyntheticValDef(UniqueName.fresh(tree.symbol.name.toTermName).toTermName, selectArg)
i += 1
Expand Down Expand Up @@ -357,7 +358,9 @@ class ReifyQuotes extends MacroTransform {
val tree2 = transform(tree)
capturers --= outer.localSymbols

seq(captured.result().valuesIterator.toList, tree2)
val captures = captured.result().valuesIterator.toList
if (captures.isEmpty) tree2
else Block(captures, tree2)
}

/** Returns true if this tree will be captured by `makeLambda`. Checks phase consistency and presence of capturer. */
Expand Down
79 changes: 47 additions & 32 deletions compiler/src/dotty/tools/dotc/transform/Splicer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,18 @@ object Splicer {
val interpreter = new Interpreter(pos, classLoader)
try {
// Some parts of the macro are evaluated during the unpickling performed in quotedExprToTree
val interpretedExpr = interpreter.interpret[scala.quoted.Expr[Any]](tree)
interpretedExpr.fold(tree)(x => PickledQuotes.quotedExprToTree(x))
val interpretedExpr = interpreter.interpret[scala.quoted.QuoteContext => scala.quoted.Expr[Any]](tree)
interpretedExpr.fold(tree)(macroClosure => PickledQuotes.quotedExprToTree(macroClosure(new scala.quoted.QuoteContext(ReflectionImpl(ctx)))))
}
catch {
case ex: StopInterpretation =>
ctx.error(ex.msg, ex.pos)
EmptyTree
case NonFatal(ex) =>
val msg =
s"""Failed to evaluate macro.
| Caused by ${ex.getClass}: ${if (ex.getMessage == null) "" else ex.getMessage}
| ${ex.getStackTrace.takeWhile(_.getClassName != "dotty.tools.dotc.transform.Splicer$").init.mkString("\n ")}
| ${ex.getStackTrace.takeWhile(_.getClassName != "dotty.tools.dotc.transform.Splicer$").drop(1).mkString("\n ")}
""".stripMargin
ctx.error(msg, pos)
EmptyTree
Expand All @@ -65,19 +68,24 @@ object Splicer {
def checkValidMacroBody(tree: Tree)(implicit ctx: Context): Unit = tree match {
case Quoted(_) => // ok
case _ =>
def checkValidStat(tree: Tree): Unit = tree match {
type Env = Set[Symbol]

def checkValidStat(tree: Tree) given Env: Env = tree match {
case tree: ValDef if tree.symbol.is(Synthetic) =>
// Check val from `foo(j = x, i = y)` which it is expanded to
// `val j$1 = x; val i$1 = y; foo(i = i$1, j = j$1)`
checkIfValidArgument(tree.rhs)
the[Env] + tree.symbol
case _ =>
ctx.error("Macro should not have statements", tree.sourcePos)
the[Env]
}
def checkIfValidArgument(tree: Tree): Unit = tree match {

def checkIfValidArgument(tree: Tree) given Env: Unit = tree match {
case Block(Nil, expr) => checkIfValidArgument(expr)
case Typed(expr, _) => checkIfValidArgument(expr)

case Apply(TypeApply(fn, _), quoted :: Nil) if fn.symbol == defn.InternalQuoted_exprQuote =>
case Apply(Select(Apply(fn, quoted :: Nil), nme.apply), _) if fn.symbol == defn.InternalQuoted_exprQuote =>
// OK

case TypeApply(fn, quoted :: Nil) if fn.symbol == defn.InternalQuoted_typeQuote =>
Expand All @@ -101,7 +109,7 @@ object Splicer {
case SeqLiteral(elems, _) =>
elems.foreach(checkIfValidArgument)

case tree: Ident if tree.symbol.is(Inline) || tree.symbol.is(Synthetic) =>
case tree: Ident if tree.symbol.is(Inline) || the[Env].contains(tree.symbol) =>
// OK

case _ =>
Expand All @@ -114,10 +122,14 @@ object Splicer {
| * Literal values of primitive types
|""".stripMargin, tree.sourcePos)
}
def checkIfValidStaticCall(tree: Tree): Unit = tree match {

def checkIfValidStaticCall(tree: Tree) given Env: Unit = tree match {
case closureDef(ddef @ DefDef(_, Nil, (ev :: Nil) :: Nil, _, _)) if ddef.symbol.info.isContextualMethod =>
checkIfValidStaticCall(ddef.rhs) given (the[Env] + ev.symbol)

case Block(stats, expr) =>
stats.foreach(checkValidStat)
checkIfValidStaticCall(expr)
val newEnv = stats.foldLeft(the[Env])((env, stat) => checkValidStat(stat) given env)
checkIfValidStaticCall(expr) given newEnv

case Typed(expr, _) =>
checkIfValidStaticCall(expr)
Expand All @@ -126,6 +138,8 @@ object Splicer {
if (fn.symbol.isConstructor && fn.symbol.owner.owner.is(Package)) ||
fn.symbol.is(Module) || fn.symbol.isStatic ||
(fn.qualifier.symbol.is(Module) && fn.qualifier.symbol.isStatic) =>
if (fn.symbol.flags.is(Inline))
ctx.error("Macro cannot be implemented with an `inline` method", fn.sourcePos)
args.flatten.foreach(checkIfValidArgument)

case _ =>
Expand All @@ -136,35 +150,29 @@ object Splicer {
|""".stripMargin, tree.sourcePos)
}

checkIfValidStaticCall(tree)
checkIfValidStaticCall(tree) given Set.empty
}

/** Tree interpreter that evaluates the tree */
private class Interpreter(pos: SourcePosition, classLoader: ClassLoader)(implicit ctx: Context) {

type Env = Map[Name, Object]
type Env = Map[Symbol, Object]

/** Returns the interpreted result of interpreting the code a call to the symbol with default arguments.
* Return Some of the result or None if some error happen during the interpretation.
*/
def interpret[T](tree: Tree)(implicit ct: ClassTag[T]): Option[T] = {
try {
interpretTree(tree)(Map.empty) match {
case obj: T => Some(obj)
case obj =>
// TODO upgrade to a full type tag check or something similar
ctx.error(s"Interpreted tree returned a result of an unexpected type. Expected ${ct.runtimeClass} but was ${obj.getClass}", pos)
None
}
} catch {
case ex: StopInterpretation =>
ctx.error(ex.msg, ex.pos)
interpretTree(tree)(Map.empty) match {
case obj: T => Some(obj)
case obj =>
// TODO upgrade to a full type tag check or something similar
ctx.error(s"Interpreted tree returned a result of an unexpected type. Expected ${ct.runtimeClass} but was ${obj.getClass}", pos)
None
}
}

def interpretTree(tree: Tree)(implicit env: Env): Object = tree match {
case Apply(TypeApply(fn, _), quoted :: Nil) if fn.symbol == defn.InternalQuoted_exprQuote =>
case Apply(Select(Apply(TypeApply(fn, _), quoted :: Nil), nme.apply), _) if fn.symbol == defn.InternalQuoted_exprQuote =>
val quoted1 = quoted match {
case quoted: Ident if quoted.symbol.isAllOf(InlineByNameProxy) =>
// inline proxy for by-name parameter
Expand Down Expand Up @@ -199,14 +207,17 @@ object Splicer {
val staticMethodCall = interpretedStaticMethodCall(fn.qualifier.symbol.moduleClass, fn.symbol)
staticMethodCall(args.flatten.map(interpretTree))
}
} else if (env.contains(fn.name)) {
env(fn.name)
} else if (env.contains(fn.symbol)) {
env(fn.symbol)
} else if (tree.symbol.is(InlineProxy)) {
interpretTree(tree.symbol.defTree.asInstanceOf[ValOrDefDef].rhs)
} else {
unexpectedTree(tree)
}

case closureDef((ddef @ DefDef(_, _, (arg :: Nil) :: Nil, _, _))) =>
(obj: AnyRef) => interpretTree(ddef.rhs) given env.updated(arg.symbol, obj)

// Interpret `foo(j = x, i = y)` which it is expanded to
// `val j$1 = x; val i$1 = y; foo(i = i$1, j = j$1)`
case Block(stats, expr) => interpretBlock(stats, expr)
Expand All @@ -228,7 +239,7 @@ object Splicer {
var unexpected: Option[Object] = None
val newEnv = stats.foldLeft(env)((accEnv, stat) => stat match {
case stat: ValDef =>
accEnv.updated(stat.name, interpretTree(stat.rhs)(accEnv))
accEnv.updated(stat.symbol, interpretTree(stat.rhs)(accEnv))
case stat =>
if (unexpected.isEmpty)
unexpected = Some(unexpectedTree(stat))
Expand All @@ -250,7 +261,7 @@ object Splicer {
args.toSeq

private def interpretQuoteContext()(implicit env: Env): Object =
new scala.quoted.QuoteContext(ReflectionImpl(ctx, pos))
new scala.quoted.QuoteContext(ReflectionImpl(ctx))

private def interpretedStaticMethodCall(moduleClass: Symbol, fn: Symbol)(implicit env: Env): List[Object] => Object = {
val (inst, clazz) =
Expand Down Expand Up @@ -339,10 +350,13 @@ object Splicer {
throw new StopInterpretation(sw.toString, pos)
case ex: InvocationTargetException =>
val sw = new StringWriter()
sw.write("An exception occurred while executing macro expansion\n")
sw.write("An exception occurred while executing macro expansion: ")
sw.write(ex.getTargetException.getMessage)
sw.write("\n")
ex.getTargetException.printStackTrace(new PrintWriter(sw))
for (stack <- ex.getTargetException.getStackTrace.iterator.takeWhile(_.getClassName != this.getClass.getName).drop(1)) {
sw.write(stack.toString)
sw.write("\n")
}
sw.write("\n")
throw new StopInterpretation(sw.toString, pos)
}
Expand Down Expand Up @@ -404,11 +418,12 @@ object Splicer {
allParams.map(paramClass)
}

/** Exception that stops interpretation if some issue is found */
private class StopInterpretation(val msg: String, val pos: SourcePosition) extends Exception

}

/** Exception that stops interpretation if some issue is found */
private class StopInterpretation(val msg: String, val pos: SourcePosition) extends Exception

object Call {
/** Matches an expression that is either a field access or an application
* It retruns a TermRef containing field accessed or a method reference and the arguments passed to it.
Expand Down
Loading