diff --git a/docs/runtime/compiler-ir.md b/docs/runtime/compiler-ir.md index 8a80be0ca08..3fd435ee30f 100644 --- a/docs/runtime/compiler-ir.md +++ b/docs/runtime/compiler-ir.md @@ -11,8 +11,8 @@ for future goals. ## Visualization -The IR can be visualized using `-Denso.compiler.dumpIr` system property. This -will output a `.dot` file in [GraphViz](www.graphviz.org) format in the +The IR can be visualized using `--vm.D=enso.compiler.dumpIr` system property. +This will output a `.dot` file in [GraphViz](www.graphviz.org) format in the `ir-dumps` directory for each IR in the program. The _dot_ file format is a minimal textual format, that can be converted to a graphical representation using the `dot` command from the GraphViz package. For example, on Ubuntu, diff --git a/engine/runtime-compiler/src/main/java/org/enso/compiler/context/ContextUtils.java b/engine/runtime-compiler/src/main/java/org/enso/compiler/context/ContextUtils.java new file mode 100644 index 00000000000..e3d853b4906 --- /dev/null +++ b/engine/runtime-compiler/src/main/java/org/enso/compiler/context/ContextUtils.java @@ -0,0 +1,22 @@ +package org.enso.compiler.context; + +import java.util.Objects; +import java.util.function.Supplier; + +final class ContextUtils { + private ContextUtils() {} + + static V assertSame(String msg, V actual, Supplier expectedSupplier) { + assert checkEquality(actual, expectedSupplier) + : msg + "\nexpected: " + expectedSupplier.get() + "\nactual: " + actual; + return actual; + } + + private static boolean checkEquality(V actual, Supplier expectedSupplier) { + if (!Objects.equals(expectedSupplier.get(), actual)) { + return false; + } else { + return true; + } + } +} diff --git a/engine/runtime-compiler/src/main/java/org/enso/compiler/context/FramePointer.java b/engine/runtime-compiler/src/main/java/org/enso/compiler/context/FramePointer.java deleted file mode 100644 index a4ff318083d..00000000000 --- a/engine/runtime-compiler/src/main/java/org/enso/compiler/context/FramePointer.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.enso.compiler.context; - -/** - * A representation of a pointer into a stack frame at a given number of levels above the current. - */ -public record FramePointer(int parentLevel, int frameSlotIdx) { - - public FramePointer { - assert parentLevel >= 0; - assert frameSlotIdx >= 0; - } -} diff --git a/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/FrameAnalysisMeta.java b/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/FrameAnalysisMeta.java new file mode 100644 index 00000000000..94a23dd2f43 --- /dev/null +++ b/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/FrameAnalysisMeta.java @@ -0,0 +1,6 @@ +package org.enso.compiler.pass.analyse; + +import org.enso.compiler.core.ir.ProcessingPass; + +public sealed interface FrameAnalysisMeta extends ProcessingPass.Metadata + permits FramePointer, FrameVariableNames {} diff --git a/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/FramePointer.java b/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/FramePointer.java new file mode 100644 index 00000000000..425af3bf8f0 --- /dev/null +++ b/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/FramePointer.java @@ -0,0 +1,38 @@ +package org.enso.compiler.pass.analyse; + +import org.enso.compiler.core.CompilerStub; +import org.enso.compiler.core.ir.ProcessingPass; +import org.enso.persist.Persistable; +import scala.Option; + +/** + * A representation of a pointer into a stack frame at a given number of levels above the current. + */ +@Persistable(clazz = FramePointer.class, id = 1283) +public record FramePointer(int parentLevel, int frameSlotIdx) implements FrameAnalysisMeta { + + public FramePointer { + assert parentLevel >= 0; + assert frameSlotIdx >= 0; + } + + @Override + public String metadataName() { + return getClass().getSimpleName(); + } + + @Override + public ProcessingPass.Metadata prepareForSerialization(CompilerStub compiler) { + return this; + } + + @Override + public Option restoreFromSerialization(CompilerStub compiler) { + return Option.apply(this); + } + + @Override + public Option duplicate() { + return Option.apply(this); + } +} diff --git a/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/FrameVariableNames.java b/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/FrameVariableNames.java new file mode 100644 index 00000000000..9d87a57a177 --- /dev/null +++ b/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/FrameVariableNames.java @@ -0,0 +1,45 @@ +package org.enso.compiler.pass.analyse; + +import java.util.List; +import org.enso.compiler.core.CompilerStub; +import org.enso.compiler.core.ir.ProcessingPass; +import org.enso.persist.Persistable; +import scala.Option; +import scala.jdk.javaapi.CollectionConverters; + +@Persistable(id = 1286) +public final class FrameVariableNames implements FrameAnalysisMeta { + private final List names; + + public FrameVariableNames(List variableNames) { + this.names = variableNames; + } + + public static FrameVariableNames create(scala.collection.immutable.List names) { + return new FrameVariableNames(CollectionConverters.asJava(names)); + } + + public List variableNames() { + return names; + } + + @Override + public String metadataName() { + return getClass().getSimpleName(); + } + + @Override + public ProcessingPass.Metadata prepareForSerialization(CompilerStub compiler) { + return this; + } + + @Override + public Option restoreFromSerialization(CompilerStub compiler) { + return Option.apply(this); + } + + @Override + public Option duplicate() { + return Option.apply(new FrameVariableNames(names)); + } +} diff --git a/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/PassPersistance.java b/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/PassPersistance.java index 01af38d2aa0..5e7ed95963a 100644 --- a/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/PassPersistance.java +++ b/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/PassPersistance.java @@ -1,7 +1,6 @@ package org.enso.compiler.pass.analyse; import java.io.IOException; -import org.enso.compiler.context.FramePointer; import org.enso.compiler.pass.analyse.alias.AliasMetadata; import org.enso.compiler.pass.analyse.alias.graph.Graph; import org.enso.compiler.pass.analyse.alias.graph.GraphOccurrence; @@ -67,8 +66,6 @@ import scala.Tuple2$; @Persistable(clazz = Graph.Link.class, id = 1266, allowInlining = false) @Persistable(clazz = TypeInference.class, id = 1280) @Persistable(clazz = FramePointerAnalysis$.class, id = 1281) -@Persistable(clazz = FramePointerAnalysis.FramePointerMeta.class, id = 1282) -@Persistable(clazz = FramePointer.class, id = 1283) public final class PassPersistance { private PassPersistance() {} @@ -155,19 +152,16 @@ public final class PassPersistance { @SuppressWarnings("unchecked") protected Graph readObject(Input in) throws IOException { - var g = new Graph(); var rootScope = (Graph.Scope) in.readObject(); assignParents(rootScope); - g.rootScope_$eq(rootScope); var links = (scala.collection.immutable.Set) in.readInline(scala.collection.immutable.Set.class); - g.initLinks(links); var nextIdCounter = in.readInt(); - g.nextIdCounter_$eq(nextIdCounter); - + var g = new Graph(rootScope, nextIdCounter, links); + g.freeze(); return g; } diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/context/LocalScope.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/context/LocalScope.scala index 828c1aac9af..4054d26b5ea 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/context/LocalScope.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/context/LocalScope.scala @@ -1,5 +1,8 @@ package org.enso.compiler.context +import org.enso.compiler.pass.analyse.FrameAnalysisMeta +import org.enso.compiler.pass.analyse.FramePointer +import org.enso.compiler.pass.analyse.FrameVariableNames import org.enso.compiler.pass.analyse.DataflowAnalysis import org.enso.compiler.pass.analyse.alias.graph.{ GraphOccurrence, @@ -7,6 +10,7 @@ import org.enso.compiler.pass.analyse.alias.graph.{ } import scala.jdk.CollectionConverters._ +import java.util.function.BiFunction /** A representation of an Enso local scope. * @@ -34,12 +38,47 @@ class LocalScope( final val aliasingGraph: () => AliasGraph, final private val scopeProvider: () => AliasGraph.Scope, final private val dataflowInfoProvider: () => DataflowAnalysis.Metadata, - final val flattenToParent: Boolean = false, - private val parentFrameSlotIdxs: Map[AliasGraph.Id, Int] = Map() + final private val symbolsProvider: () => FrameAnalysisMeta = null, + final val flattenToParent: Boolean = false, + private val parentFrameSlotIdxs: () => Map[AliasGraph.Id, Int] = () => Map() ) { lazy val scope: AliasGraph.Scope = scopeProvider() lazy val dataflowInfo: DataflowAnalysis.Metadata = dataflowInfoProvider() + /** Computes allSymbols needed by this scope. Either the value is obtained + * from symbolsProvider or (as a fallback) a computation from aliasingGraph + * is performed - the latter situation is logged, when log is not null. + * + * @param where human "friendly" identification of the caller + * @param log function to log or null if no logging is needed + */ + def allSymbols( + where: String, + log: BiFunction[String, Array[Object], Void] + ): java.util.List[String] = { + def symbols(): java.util.List[String] = { + val r = scope.allDefinitions.map(_.symbol) + r.asJava + } + val meta = if (symbolsProvider == null) null else symbolsProvider() + if (meta.isInstanceOf[FrameVariableNames]) { + val cached = meta.asInstanceOf[FrameVariableNames].variableNames() + if (log != null) { + ContextUtils.assertSame(where, cached, () => symbols()) + } + cached + } else { + val result = symbols() + if (log != null) { + log( + "Scope computed from AliasAnalysis at {0} = {1}", + Array(where, result) + ) + } + result + } + } + private lazy val localFrameSlotIdxs: Map[AliasGraph.Id, Int] = gatherLocalFrameSlotIdxs() @@ -47,7 +86,7 @@ class LocalScope( * Useful for quick searching for [[FramePointer]] of parent scopes. */ private lazy val allFrameSlotIdxs: Map[AliasGraph.Id, Int] = - parentFrameSlotIdxs ++ localFrameSlotIdxs + parentFrameSlotIdxs() ++ localFrameSlotIdxs /** Creates a new child with a new aliasing scope. * @@ -64,15 +103,23 @@ class LocalScope( */ def createChild( childScope: () => AliasGraph.Scope, - flattenToParent: Boolean = false + flattenToParent: Boolean = false, + symbolsProvider: () => FrameVariableNames = null ): LocalScope = { + val sp = if (flattenToParent) { + assert(symbolsProvider == null) + this.symbolsProvider + } else { + symbolsProvider + } new LocalScope( Some(this), aliasingGraph, childScope, () => dataflowInfo, + sp, flattenToParent, - allFrameSlotIdxs + () => allFrameSlotIdxs ) } @@ -125,11 +172,27 @@ class LocalScope( } object LocalScope { - /** Constructs a local scope for an [[EnsoRootNode]]. - * - * @return a defaulted local scope + /** Empty and immutable singleton scope. */ - def root: LocalScope = { + val empty: LocalScope = { + val graph = new AliasGraph + graph.freeze() + val info = DataflowAnalysis.DependencyInfo() + val emptyVariableNames = FrameVariableNames.create(List()) + new LocalScope( + None, + () => graph, + () => graph.rootScope, + () => info, + () => emptyVariableNames + ) + } + + /** Constructs a new local scope for an [[EnsoRootNode]] that can then be modified. + * + * @return a new empty scope ready for additional modifications. + */ + def createEmpty: LocalScope = { val graph = new AliasGraph val info = DataflowAnalysis.DependencyInfo() new LocalScope( diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/FramePointerAnalysis.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/FramePointerAnalysis.scala index 190f1cb582c..5d477d4efaa 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/FramePointerAnalysis.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/FramePointerAnalysis.scala @@ -1,12 +1,7 @@ package org.enso.compiler.pass.analyse -import org.enso.compiler.context.{ - CompilerContext, - FramePointer, - InlineContext, - LocalScope, - ModuleContext -} +import org.enso.compiler.pass.analyse.FramePointer +import org.enso.compiler.context.{InlineContext, LocalScope, ModuleContext} import org.enso.compiler.core.ir.Name.GenericAnnotation import org.enso.compiler.core.{CompilerError, IR} import org.enso.compiler.core.ir.expression.{Application, Case} @@ -17,13 +12,11 @@ import org.enso.compiler.core.ir.{ Function, Module, Name, - Pattern, - ProcessingPass + Pattern } import org.enso.compiler.core.ir.module.scope.Definition import org.enso.compiler.core.ir.module.scope.definition.Method import org.enso.compiler.pass.IRPass -import org.enso.compiler.pass.IRPass.IRMetadata import org.enso.compiler.pass.analyse.alias.AliasMetadata import org.enso.compiler.pass.analyse.alias.graph.{Graph, GraphOccurrence} @@ -33,7 +26,7 @@ import org.enso.compiler.pass.analyse.alias.graph.{Graph, GraphOccurrence} */ case object FramePointerAnalysis extends IRPass { - override type Metadata = FramePointerMeta + override type Metadata = FrameAnalysisMeta override type Config = IRPass.Configuration.Default @@ -54,14 +47,18 @@ case object FramePointerAnalysis extends IRPass { ir match { case m: Method.Explicit => getAliasAnalysisGraph(m) match { - case Some(graph) => + case Some( + graph + ) => processExpression(m.body, graph) + updateSymbolNames(m, graph.rootScope) case _ => () } case m: Method.Conversion => getAliasAnalysisGraph(m) match { case Some(graph) => processExpression(m.body, graph) + updateSymbolNames(m, graph.rootScope) case _ => () } case t: Definition.Type => @@ -78,7 +75,9 @@ case object FramePointerAnalysis extends IRPass { member.annotations.foreach { annotation => processAnnotation(annotation, memberGraph) } + updateSymbolNames(member, memberGraph.rootScope) } + updateSymbolNames(t, graph.rootScope) case _ => () } case annot: GenericAnnotation => @@ -94,6 +93,11 @@ case object FramePointerAnalysis extends IRPass { } } + private def updateSymbolNames(e: IR, s: Graph.Scope): Unit = { + val symbols = s.allDefinitions.map(_.symbol) + updateMeta(e, FrameVariableNames.create(symbols)) + } + private def processAnnotation( annot: GenericAnnotation, graph: Graph @@ -103,6 +107,7 @@ case object FramePointerAnalysis extends IRPass { rootScope.graph case None => graph } + updateSymbolNames(annot, annotGraph.rootScope) processExpression(annot.expression, annotGraph) } @@ -123,7 +128,8 @@ case object FramePointerAnalysis extends IRPass { case Some(defaultValue) => getAliasAnalysisGraph(defaultValue) match { case Some(defaultValueGraph) => - processExpression(defaultValue, defaultValueGraph) + processExpression(defaultValue, defaultValueGraph, false) + maybAttachFrameVariableNames(defaultValue) case None => processExpression(defaultValue, graph) } @@ -134,7 +140,8 @@ case object FramePointerAnalysis extends IRPass { private def processExpression( exprIr: Expression, - graph: Graph + graph: Graph, + updateSymbols: Boolean = true ): Unit = { exprIr match { case name: Name => maybeAttachFramePointer(name, graph) @@ -147,7 +154,6 @@ case object FramePointerAnalysis extends IRPass { processArgumentDefs(args, graph) processExpression(body, graph) case binding @ Expression.Binding(name, expr, _, _) => - maybeAttachFramePointer(binding, graph) maybeAttachFramePointer(name, graph) processExpression(expr, graph) maybeAttachFramePointer(binding, graph) @@ -159,6 +165,9 @@ case object FramePointerAnalysis extends IRPass { } case _ => () } + if (updateSymbols) { + maybAttachFrameVariableNames(exprIr) + } } private def processCaseBranch( @@ -170,6 +179,7 @@ case object FramePointerAnalysis extends IRPass { "An alias analysis graph is expected on " + branch ) case Some(graph) => + maybAttachFrameVariableNames(branch) processExpression(branch.expression, graph) processCasePattern(branch.pattern, graph) } @@ -197,6 +207,7 @@ case object FramePointerAnalysis extends IRPass { case _: Pattern.Documentation => () case _ => () } + updateSymbolNames(pattern, graph.rootScope) } private def processApplication( @@ -230,10 +241,19 @@ case object FramePointerAnalysis extends IRPass { arguments.foreach { case arg @ CallArgument.Specified(name, value, _, _) => maybeAttachFramePointer(arg, graph) name.foreach(maybeAttachFramePointer(_, graph)) - processExpression(value, graph) + processExpression(value, graph, false) + maybAttachFrameVariableNames(value) + maybAttachFrameVariableNames(arg) } } + private def maybAttachFrameVariableNames(ir: IR): Unit = { + getAliasRootScope(ir).foreach(root => + updateSymbolNames(ir, root.graph.rootScope) + ) + getAliasChildScope(ir).foreach(child => updateSymbolNames(ir, child.scope)) + } + /** Attaches [[FramePointerMeta]] metadata to the given `ir` if there is an * appropriate [[AliasMetadata.Occurrence]] already attached to it. * @param ir IR to attach the frame pointer metadata to. @@ -263,7 +283,10 @@ case object FramePointerAnalysis extends IRPass { val parentLevel = getScopeDistance(defScope, scope) val frameSlotIdx = getFrameSlotIdxInScope(graph, defScope, defOcc) - updateMeta(ir, new FramePointer(parentLevel, frameSlotIdx)) + updateMeta( + ir, + new FramePointer(parentLevel, frameSlotIdx) + ) case None => // It is possible that there is no Def for this Use. It can, for example, be // Use for some global symbol. In `IrToTruffle`, an UnresolvedSymbol will be @@ -275,7 +298,10 @@ case object FramePointerAnalysis extends IRPass { // The definition cannot write to parent's frame slots. val parentLevel = 0 val frameSlotIdx = getFrameSlotIdxInScope(graph, scope, defn) - updateMeta(ir, new FramePointer(parentLevel, frameSlotIdx)) + updateMeta( + ir, + new FramePointer(parentLevel, frameSlotIdx) + ) case _ => () } case _ => () @@ -286,9 +312,19 @@ case object FramePointerAnalysis extends IRPass { private def updateMeta( ir: IR, - framePointer: FramePointer + newMeta: FrameAnalysisMeta ): Unit = { - ir.passData().update(this, new FramePointerMeta(framePointer)) + ir.passData().get(this) match { + case None => + ir.passData() + .update(this, newMeta) + case Some(meta) => + val ex = new IllegalStateException( + "Unexpected FrameAnalysisMeta associated with IR " + ir + "\nOld: " + meta + " new " + newMeta + ) + ex.setStackTrace(ex.getStackTrace().slice(0, 10)) + throw ex + } } /** Returns the index of the given `defOcc` definition in the given `scope` @@ -357,6 +393,15 @@ case object FramePointerAnalysis extends IRPass { } } + private def getAliasChildScope( + ir: IR + ): Option[AliasMetadata.ChildScope] = { + ir.passData() + .get(AliasAnalysis) + .filter(_.isInstanceOf[AliasMetadata.ChildScope]) + .map(_.asInstanceOf[AliasMetadata.ChildScope]) + } + private def getAliasAnalysisGraph( ir: IR ): Option[Graph] = { @@ -380,36 +425,4 @@ case object FramePointerAnalysis extends IRPass { exprIr } } - - // === Pass Configuration =================================================== - - class FramePointerMeta( - val framePointer: FramePointer - ) extends IRMetadata { - override val metadataName: String = "FramePointer" - - def parentLevel(): Int = framePointer.parentLevel - - def frameSlotIdx(): Int = framePointer.frameSlotIdx - - /** @inheritdoc - */ - override def duplicate(): Option[Metadata] = { - Some(new FramePointerMeta(framePointer)) - } - - /** @inheritdoc - */ - override def prepareForSerialization( - compiler: CompilerContext - ): ProcessingPass.Metadata = this - - /** @inheritdoc - */ - override def restoreFromSerialization( - compiler: CompilerContext - ): Option[ProcessingPass.Metadata] = Some(this) - - override def toString: String = s"FramePointerMeta($framePointer)" - } } diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/alias/graph/Graph.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/alias/graph/Graph.scala index 066cdba3e70..edf167ef724 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/alias/graph/Graph.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/alias/graph/Graph.scala @@ -9,44 +9,53 @@ import scala.collection.mutable import scala.reflect.ClassTag /** A graph containing aliasing information for a given root scope in Enso. */ -sealed class Graph extends Serializable { - var rootScope: Graph.Scope = new Graph.Scope() - private var links: Set[Graph.Link] = Set() +sealed class Graph( + val rootScope: Graph.Scope = new Graph.Scope(), + private var _nextIdCounter: Int = 0, + private var links: Set[Graph.Link] = Set() +) extends Serializable { private var sourceLinks: Map[Graph.Id, Set[Graph.Link]] = new HashMap() private var targetLinks: Map[Graph.Id, Set[Graph.Link]] = new HashMap() - var nextIdCounter = 0 + private var frozen: Boolean = false + + { + links.foreach(addSourceTargetLink) + } private var globalSymbols: Map[Graph.Symbol, GraphOccurrence.Global] = Map() + /** @return the next counter value + */ + def nextIdCounter: Int = _nextIdCounter + /** @return a deep structural copy of `this` */ def deepCopy( scope_mapping: mutable.Map[Scope, Scope] = mutable.Map() ): Graph = { - val copy = new Graph - copy.rootScope = this.rootScope.deepCopy(scope_mapping) + val copy = new Graph( + this.rootScope.deepCopy(scope_mapping), + this.nextIdCounter + ) copy.links = this.links copy.sourceLinks = this.sourceLinks copy.targetLinks = this.targetLinks copy.globalSymbols = this.globalSymbols - copy.nextIdCounter = this.nextIdCounter copy } - def initLinks(links: Set[Graph.Link]): Unit = { - sourceLinks = new HashMap() - targetLinks = new HashMap() - links.foreach(addSourceTargetLink) - this.links = links - } - def getLinks(): Set[Graph.Link] = links + def freeze(): Unit = { + frozen = true + } + /** Registers a requested global symbol in the aliasing scope. * * @param sym the symbol occurrence */ def addGlobalSymbol(sym: GraphOccurrence.Global): Unit = { + assert(!frozen) if (!globalSymbols.contains(sym.symbol)) { globalSymbols = globalSymbols + (sym.symbol -> sym) } @@ -57,12 +66,13 @@ sealed class Graph extends Serializable { * @return a copy of the graph structure */ def copy: Graph = { - val graph = new Graph - graph.links = links - graph.sourceLinks = sourceLinks - graph.targetLinks = targetLinks - graph.rootScope = rootScope.deepCopy(mutable.Map()) - graph.nextIdCounter = nextIdCounter + val graph = new Graph( + rootScope.deepCopy(mutable.Map()), + nextIdCounter + ) + graph.links = links + graph.sourceLinks = sourceLinks + graph.targetLinks = targetLinks graph } @@ -84,8 +94,8 @@ sealed class Graph extends Serializable { * @return a unique identifier for this graph */ def nextId(): Graph.Id = { - val nextId = nextIdCounter - nextIdCounter += 1 + val nextId = _nextIdCounter + _nextIdCounter += 1 nextId } @@ -99,13 +109,14 @@ sealed class Graph extends Serializable { occurrence: GraphOccurrence.Use ): Option[Graph.Link] = { scopeFor(occurrence.id).flatMap(_.resolveUsage(occurrence).map { link => - links += link addSourceTargetLink(link) + links += link link }) } private def addSourceTargetLink(link: Graph.Link): Unit = { + assert(!frozen) sourceLinks = sourceLinks.updatedWith(link.source)(v => v.map(s => s + link).orElse(Some(Set(link))) ) diff --git a/engine/runtime-instrument-common/src/test/scala/org/enso/compiler/test/context/ChangesetBuilderTest.scala b/engine/runtime-instrument-common/src/test/scala/org/enso/compiler/test/context/ChangesetBuilderTest.scala index f50c44759fe..3381178a6b6 100644 --- a/engine/runtime-instrument-common/src/test/scala/org/enso/compiler/test/context/ChangesetBuilderTest.scala +++ b/engine/runtime-instrument-common/src/test/scala/org/enso/compiler/test/context/ChangesetBuilderTest.scala @@ -488,7 +488,7 @@ class ChangesetBuilderTest def freshInlineContext: InlineContext = buildInlineContext( - localScope = Some(LocalScope.root), + localScope = Some(LocalScope.createEmpty), freshNameSupply = Some(new FreshNameSupply), isInTailPosition = Some(false) ) diff --git a/engine/runtime-instrument-repl-debugger/src/main/java/org/enso/interpreter/instrument/ReplDebuggerInstrument.java b/engine/runtime-instrument-repl-debugger/src/main/java/org/enso/interpreter/instrument/ReplDebuggerInstrument.java index 46efd90482e..4cdc0122ace 100644 --- a/engine/runtime-instrument-repl-debugger/src/main/java/org/enso/interpreter/instrument/ReplDebuggerInstrument.java +++ b/engine/runtime-instrument-repl-debugger/src/main/java/org/enso/interpreter/instrument/ReplDebuggerInstrument.java @@ -30,7 +30,7 @@ import java.util.HashMap; import java.util.Map; import java.util.logging.Level; import org.enso.common.DebugServerInfo; -import org.enso.compiler.context.FramePointer; +import org.enso.compiler.pass.analyse.FramePointer; import org.enso.interpreter.node.EnsoRootNode; import org.enso.interpreter.node.expression.builtin.debug.DebugBreakpointNode; import org.enso.interpreter.node.expression.builtin.text.util.ToJavaStringNode; diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExecCompilerTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExecCompilerTest.java index 592ec81905d..a6a0068896a 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExecCompilerTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExecCompilerTest.java @@ -442,6 +442,48 @@ public class ExecCompilerTest { } } + @Test + public void testFnAsADefaultValue() throws Exception { + var code = + """ + type N + type T + V (r:(T -> N | T)=(_->N)) + + v self = self.r self + + run type = case type of + 0 -> T.V + 1 -> T.V (_->N) + """; + var module = ctx.eval(LanguageInfo.ID, code); + var run = module.invokeMember("eval_expression", "run"); + var real = run.execute(1L); + var realN = real.invokeMember("v"); + var defaulted = run.execute(0L); + var defaultedN = defaulted.invokeMember("v"); + assertEquals("Should be the same", realN, defaultedN); + } + + @Test + public void testTemporaryFileSpecProblem() throws Exception { + var code = + """ + from Standard.Base.Errors.Common import all + + run t = F.app f-> + f.read t + + type F + read self r = r + app fn = fn F + """; + var module = ctx.eval(LanguageInfo.ID, code); + var run = module.invokeMember("eval_expression", "run"); + var real = run.execute(1L); + assertEquals("Should be the same", 1, real.asInt()); + } + @Test public void testPropertlyIdentifyNameOfJavaClassInError() throws Exception { var module = diff --git a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/analyse/DataflowAnalysisTest.scala b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/analyse/DataflowAnalysisTest.scala index 48a1ecf7369..7584fc8f37d 100644 --- a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/analyse/DataflowAnalysisTest.scala +++ b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/analyse/DataflowAnalysisTest.scala @@ -161,7 +161,7 @@ class DataflowAnalysisTest extends CompilerTest { */ def mkInlineContext: InlineContext = { buildInlineContext( - localScope = Some(LocalScope.root), + localScope = Some(LocalScope.createEmpty), isInTailPosition = Some(false), freshNameSupply = Some(new FreshNameSupply) ) diff --git a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/analyse/DemandAnalysisTest.scala b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/analyse/DemandAnalysisTest.scala index 6fd9e7b3346..79904d5656d 100644 --- a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/analyse/DemandAnalysisTest.scala +++ b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/analyse/DemandAnalysisTest.scala @@ -71,7 +71,7 @@ class DemandAnalysisTest extends CompilerTest { */ def mkInlineContext: InlineContext = { buildInlineContext( - localScope = Some(LocalScope.root), + localScope = Some(LocalScope.createEmpty), freshNameSupply = Some(new FreshNameSupply) ) } diff --git a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/analyse/FramePointerAnalysisTest.scala b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/analyse/FramePointerAnalysisTest.scala index 40e13a43404..64bbd79cedb 100644 --- a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/analyse/FramePointerAnalysisTest.scala +++ b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/analyse/FramePointerAnalysisTest.scala @@ -1,7 +1,9 @@ package org.enso.compiler.test.pass.analyse import org.enso.compiler.Passes -import org.enso.compiler.context.{FramePointer, FreshNameSupply, ModuleContext} +import org.enso.compiler.pass.analyse.FramePointer +import org.enso.compiler.pass.analyse.FrameAnalysisMeta +import org.enso.compiler.context.{FreshNameSupply, ModuleContext} import org.enso.compiler.core.IR import org.enso.compiler.core.ir.{ CallArgument, @@ -87,10 +89,10 @@ class FramePointerAnalysisTest extends CompilerTest { withClue("Expression.Binding must have FramePointer associated") { allOcc.head._1 .unsafeGetMetadata(FramePointerAnalysis, "should exist") - .framePointer shouldEqual new FramePointer(0, 1) + .asInstanceOf[FramePointer] shouldEqual new FramePointer(0, 1) allOcc.last._1 .unsafeGetMetadata(FramePointerAnalysis, "should exist") - .framePointer shouldEqual new FramePointer(0, 2) + .asInstanceOf[FramePointer] shouldEqual new FramePointer(0, 2) } } @@ -129,7 +131,7 @@ class FramePointerAnalysisTest extends CompilerTest { "There should be no associated FramePointer with usage of `+`, because it is not defined " + "in any scope" ) { - plusUseIr.passData().get(FramePointerAnalysis) shouldNot be(defined) + findFP(plusUseIr) shouldNot be(defined) } val framePointers = collectAllFramePointers(ir) framePointers.size shouldBe 4 @@ -429,7 +431,7 @@ class FramePointerAnalysisTest extends CompilerTest { lit => lit.name == "My_Type" ).last withClue("No frame pointer attached to a symbol with global occurence") { - myTypeLit.passData.get(FramePointerAnalysis) shouldNot be(defined) + findFP(myTypeLit) shouldNot be(defined) } withClue("There is a Use occurence") { myTypeLit.passData.get(AliasAnalysis) shouldBe defined @@ -455,7 +457,7 @@ class FramePointerAnalysisTest extends CompilerTest { lit => lit.name == "My_Type" ).apply(1) withClue("No frame pointer attached to a symbol with global occurence") { - myTypeLit.passData.get(FramePointerAnalysis) shouldNot be(defined) + findFP(myTypeLit) shouldNot be(defined) } withClue("There is a Use occurence") { myTypeLit.passData.get(AliasAnalysis) shouldBe defined @@ -466,6 +468,10 @@ class FramePointerAnalysisTest extends CompilerTest { } } + private def findFP(ir: IR) = ir.passData + .get(FramePointerAnalysis) + .filter(meta => meta.isInstanceOf[FramePointer]) + /** Find the first IR element of the given `T` type by the given `filterCondition`. * @param filterCondition Filter condition will be applied to all the elements of the desired type. * The first element that matches the condition will be returned @@ -513,7 +519,7 @@ class FramePointerAnalysisTest extends CompilerTest { } ir .unsafeGetMetadata(FramePointerAnalysis, "should exist") - .framePointer shouldEqual framePointer + .asInstanceOf[FramePointer] shouldEqual framePointer } private def findAssociatedIr( @@ -548,10 +554,11 @@ class FramePointerAnalysisTest extends CompilerTest { private def collectAllFramePointers( ir: IR - ): List[(IR, FramePointerAnalysis.Metadata)] = { + ): List[(IR, FrameAnalysisMeta)] = { ir.preorder().flatMap { childIr => childIr.getMetadata(FramePointerAnalysis) match { - case Some(framePointerMeta: FramePointerAnalysis.Metadata) => + case Some(framePointerMeta: FrameAnalysisMeta) + if (framePointerMeta.isInstanceOf[FramePointer]) => Some((childIr, framePointerMeta)) case _ => None } diff --git a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/analyse/TailCallTest.scala b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/analyse/TailCallTest.scala index c27037462ad..e6828b07946 100644 --- a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/analyse/TailCallTest.scala +++ b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/analyse/TailCallTest.scala @@ -31,14 +31,14 @@ class TailCallTest extends CompilerTest { def mkTailContext: InlineContext = buildInlineContext( - localScope = Some(LocalScope.root), + localScope = Some(LocalScope.createEmpty), isInTailPosition = Some(true), freshNameSupply = Some(new FreshNameSupply) ) def mkNoTailContext: InlineContext = buildInlineContext( - localScope = Some(LocalScope.root), + localScope = Some(LocalScope.createEmpty), isInTailPosition = Some(false), freshNameSupply = Some(new FreshNameSupply) ) diff --git a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/lint/UnusedBindingsTest.scala b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/lint/UnusedBindingsTest.scala index 3dee924fe36..de6d875f774 100644 --- a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/lint/UnusedBindingsTest.scala +++ b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/lint/UnusedBindingsTest.scala @@ -51,7 +51,7 @@ class UnusedBindingsTest extends CompilerTest with Inside { */ def mkInlineContext: InlineContext = { buildInlineContext( - localScope = Some(LocalScope.root), + localScope = Some(LocalScope.createEmpty), isInTailPosition = Some(false), freshNameSupply = Some(new FreshNameSupply) ) diff --git a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/optimise/LambdaConsolidateTest.scala b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/optimise/LambdaConsolidateTest.scala index 0f2bdfecc8a..41bdf17e893 100644 --- a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/optimise/LambdaConsolidateTest.scala +++ b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/optimise/LambdaConsolidateTest.scala @@ -77,7 +77,7 @@ class LambdaConsolidateTest extends CompilerTest { */ def mkContext: InlineContext = { buildInlineContext( - localScope = Some(LocalScope.root), + localScope = Some(LocalScope.createEmpty), freshNameSupply = Some(new FreshNameSupply), passConfiguration = Some(passConfiguration) ) diff --git a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/resolve/SuspendedArgumentsTest.scala b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/resolve/SuspendedArgumentsTest.scala index 8e67c933be8..d4170260aa0 100644 --- a/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/resolve/SuspendedArgumentsTest.scala +++ b/engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/resolve/SuspendedArgumentsTest.scala @@ -80,7 +80,7 @@ class SuspendedArgumentsTest extends CompilerTest { def mkInlineContext: InlineContext = { buildInlineContext( freshNameSupply = Some(new FreshNameSupply), - localScope = Some(LocalScope.root) + localScope = Some(LocalScope.createEmpty) ) } diff --git a/engine/runtime-parser/src/main/scala/org/enso/compiler/core/ir/Function.scala b/engine/runtime-parser/src/main/scala/org/enso/compiler/core/ir/Function.scala index 363ab56c388..064158a8499 100644 --- a/engine/runtime-parser/src/main/scala/org/enso/compiler/core/ir/Function.scala +++ b/engine/runtime-parser/src/main/scala/org/enso/compiler/core/ir/Function.scala @@ -84,7 +84,7 @@ object Function { Persistance.Reference.of(body, true), location, true, - ir.passData + ir.passData.duplicate() ) diagnostics = ir.diagnostics } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/EnsoLanguage.java b/engine/runtime/src/main/java/org/enso/interpreter/EnsoLanguage.java index 206144f55e1..8501af210a1 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/EnsoLanguage.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/EnsoLanguage.java @@ -267,7 +267,7 @@ public final class EnsoLanguage extends TruffleLanguage { if (optionTupple.nonEmpty()) { var newInlineContext = optionTupple.get()._1(); var ir = optionTupple.get()._2(); - var sco = newInlineContext.localScope().getOrElse(LocalScope::root); + var sco = newInlineContext.localScope().getOrElse(LocalScope::empty); var mod = newInlineContext.getModule(); var m = org.enso.interpreter.runtime.Module.fromCompilerModule(mod); var toTruffle = diff --git a/engine/runtime/src/main/java/org/enso/interpreter/caches/Cache.java b/engine/runtime/src/main/java/org/enso/interpreter/caches/Cache.java index 4200eb9957d..b0daa46e991 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/caches/Cache.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/caches/Cache.java @@ -287,7 +287,11 @@ public final class Cache { return Optional.empty(); } } catch (ClassNotFoundException | IOException ex) { - logger.log(Level.WARNING, "`" + logName + "` in " + dataPath + " failed to load: ", ex); + logger.log( + Level.WARNING, + "`{0}` in {1} failed to load: {2}", + new Object[] {logName, dataPath, ex.getMessage()}); + logger.log(Level.FINE, "`{0}` in {1} failed to load:", ex); invalidateCache(cacheRoot, logger); return Optional.empty(); } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/EnsoRootNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/EnsoRootNode.java index b202ac95262..147677d77c5 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/EnsoRootNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/EnsoRootNode.java @@ -1,5 +1,6 @@ package org.enso.interpreter.node; +import com.oracle.truffle.api.TruffleLogger; import com.oracle.truffle.api.frame.FrameDescriptor; import com.oracle.truffle.api.frame.FrameSlotKind; import com.oracle.truffle.api.nodes.NodeInfo; @@ -7,16 +8,20 @@ import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.api.source.Source; import com.oracle.truffle.api.source.SourceSection; import java.util.Objects; +import java.util.function.BiFunction; +import java.util.logging.Level; +import org.enso.common.LanguageInfo; import org.enso.compiler.context.LocalScope; import org.enso.interpreter.EnsoLanguage; import org.enso.interpreter.runtime.EnsoContext; import org.enso.interpreter.runtime.error.DataflowError; import org.enso.interpreter.runtime.scope.ModuleScope; -import org.enso.interpreter.util.ScalaConversions; /** A common base class for all kinds of root node in Enso. */ @NodeInfo(shortName = "Root", description = "A root node for Enso computations") public abstract class EnsoRootNode extends RootNode { + private static final TruffleLogger LOGGER = TruffleLogger.getLogger(LanguageInfo.ID); + private final String name; private final int sourceStartIndex; private final int sourceLength; @@ -39,7 +44,7 @@ public abstract class EnsoRootNode extends RootNode { ModuleScope moduleScope, String name, SourceSection sourceSection) { - super(language, buildFrameDescriptor(localScope)); + super(language, buildFrameDescriptor(name, localScope, LOGGER)); Objects.requireNonNull(language); Objects.requireNonNull(localScope); Objects.requireNonNull(moduleScope); @@ -62,11 +67,21 @@ public abstract class EnsoRootNode extends RootNode { * * @return {@link FrameDescriptor} built from the variable definitions in the local localScope. */ - private static FrameDescriptor buildFrameDescriptor(LocalScope localScope) { + private static FrameDescriptor buildFrameDescriptor( + String name, LocalScope localScope, TruffleLogger log) { var descriptorBuilder = FrameDescriptor.newBuilder(); descriptorBuilder.addSlot(FrameSlotKind.Object, LocalScope.monadicStateSlotName(), null); - for (var definition : ScalaConversions.asJava(localScope.scope().allDefinitions())) { - descriptorBuilder.addSlot(FrameSlotKind.Illegal, definition.symbol(), null); + + BiFunction logFnOrNull = + log.isLoggable(Level.FINE) + ? (msg, args) -> { + log.log(Level.FINE, msg, args); + return null; + } + : null; + var allDefs = localScope.allSymbols(name, logFnOrNull); + for (var definition : allDefs) { + descriptorBuilder.addSlot(FrameSlotKind.Illegal, definition, null); } descriptorBuilder.defaultValue(DataflowError.UNINITIALIZED); var frameDescriptor = descriptorBuilder.build(); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/debug/EvalNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/debug/EvalNode.java index e0a2109fc38..d4a8f180e6e 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/debug/EvalNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/debug/EvalNode.java @@ -65,7 +65,8 @@ public abstract class EvalNode extends BaseNode { @CompilerDirectives.TruffleBoundary RootCallTarget parseExpression(LocalScope scope, ModuleScope moduleScope, String expression) { EnsoContext context = EnsoContext.get(this); - LocalScope localScope = scope.createChild(); + LocalScope localScope = + scope == LocalScope.empty() ? LocalScope.createEmpty() : scope.createChild(); var compiler = context.getCompiler(); InlineContext inlineContext = InlineContext.fromJava( @@ -83,7 +84,7 @@ public abstract class EvalNode extends BaseNode { var newInlineContext = tuppleOption.get()._1(); var ir = tuppleOption.get()._2(); - var sco = newInlineContext.localScope().getOrElse(LocalScope::root); + var sco = newInlineContext.localScope().getOrElse(LocalScope::empty); var mod = newInlineContext.getModule(); var m = org.enso.interpreter.runtime.Module.fromCompilerModule(mod); var toTruffle = new IrToTruffle(context, src, m.getScopeBuilder(), compiler.getConfig()); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/scope/ReadLocalVariableNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/scope/ReadLocalVariableNode.java index 63b73723e50..1881a77f3fe 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/scope/ReadLocalVariableNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/scope/ReadLocalVariableNode.java @@ -8,7 +8,7 @@ import com.oracle.truffle.api.frame.MaterializedFrame; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.ExplodeLoop; import com.oracle.truffle.api.nodes.NodeInfo; -import org.enso.compiler.context.FramePointer; +import org.enso.compiler.pass.analyse.FramePointer; import org.enso.interpreter.node.ExpressionNode; import org.enso.interpreter.runtime.callable.function.Function; diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/Module.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/Module.java index 246f303e7e5..6cab1ab0c13 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/Module.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/Module.java @@ -660,7 +660,7 @@ public final class Module implements EnsoObject { .getBuiltinFunction( builtins.debug(), Builtins.MethodNames.Debug.EVAL, context.getLanguage()) .orElseThrow(); - CallerInfo callerInfo = new CallerInfo(null, LocalScope.root(), scope); + CallerInfo callerInfo = new CallerInfo(null, LocalScope.empty(), scope); return callOptimiserNode.executeDispatch( null, eval.getFunction(), diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/UnresolvedConstructor.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/UnresolvedConstructor.java index a7304061558..a5c768f5a79 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/UnresolvedConstructor.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/UnresolvedConstructor.java @@ -200,7 +200,7 @@ public final class UnresolvedConstructor implements EnsoObject { body.adoptChildren(); var root = ClosureRootNode.build( - lang, LocalScope.root(), scope, body, section, prototype.getName(), true, true); + lang, LocalScope.empty(), scope, body, section, prototype.getName(), true, true); root.adoptChildren(); assert Objects.equals(expr.getSourceSection(), section) : "Expr: " + expr.getSourceSection() + " orig: " + section; diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/atom/AtomConstructor.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/atom/AtomConstructor.java index 225984082ce..bedd86003ce 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/atom/AtomConstructor.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/atom/AtomConstructor.java @@ -112,7 +112,7 @@ public final class AtomConstructor implements EnsoObject { return initializeFields( language, null, - LocalScope.root(), + LocalScope.empty(), scopeBuilder, new ExpressionNode[0], reads, diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/atom/GetFieldNode.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/atom/GetFieldNode.java index 02005398484..dd0696356c7 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/atom/GetFieldNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/atom/GetFieldNode.java @@ -24,7 +24,7 @@ final class GetFieldNode extends EnsoRootNode { * @param index the index this node should use for field lookup. */ GetFieldNode(EnsoLanguage language, int index, Type type, String name, ModuleScope moduleScope) { - super(language, LocalScope.root(), moduleScope, name, null); + super(language, LocalScope.empty(), moduleScope, name, null); this.index = index; this.type = type; this.name = name; diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/atom/QualifiedAccessorNode.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/atom/QualifiedAccessorNode.java index 26872b526d0..2be3a62f495 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/atom/QualifiedAccessorNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/atom/QualifiedAccessorNode.java @@ -24,7 +24,7 @@ final class QualifiedAccessorNode extends EnsoRootNode { EnsoLanguage language, AtomConstructor atomConstructor, ModuleScope moduleScope) { super( language, - LocalScope.root(), + LocalScope.empty(), moduleScope, atomConstructor.getQualifiedName().toString(), null); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/DebugLocalScope.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/DebugLocalScope.java index d1b1359d954..dcec2f91e0a 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/DebugLocalScope.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/DebugLocalScope.java @@ -16,7 +16,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.stream.Collectors; -import org.enso.compiler.context.FramePointer; +import org.enso.compiler.pass.analyse.FramePointer; import org.enso.interpreter.EnsoLanguage; import org.enso.interpreter.node.EnsoRootNode; import org.enso.interpreter.runtime.callable.function.Function; diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/TopLevelScope.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/TopLevelScope.java index 0dd5629983f..37d03116996 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/TopLevelScope.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/TopLevelScope.java @@ -179,7 +179,9 @@ public final class TopLevelScope implements EnsoObject { } catch (InterruptedException e) { throw new RuntimeException(e); } catch (ExecutionException e) { - throw new RuntimeException(e); + var re = new RuntimeException(e); + re.setStackTrace(e.getStackTrace()); + throw re; } } diff --git a/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala b/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala index b7103a4e70e..fa2a01f0f46 100644 --- a/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala +++ b/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala @@ -1,10 +1,12 @@ package org.enso.interpreter.runtime +import java.util.logging.Level import com.oracle.truffle.api.source.{Source, SourceSection} import com.oracle.truffle.api.interop.InteropLibrary +import org.enso.compiler.pass.analyse.FramePointer +import org.enso.compiler.pass.analyse.FrameVariableNames import org.enso.compiler.context.{ CompilerContext, - FramePointer, LocalScope, NameResolutionAlgorithm } @@ -264,30 +266,28 @@ class IrToTruffle( atomCons: AtomConstructor, atomDefn: Definition.Data ): Unit = { - def scopeInfo() = { - atomDefn - .unsafeGetMetadata( - AliasAnalysis, - "No root scope on an atom definition." - ) - .unsafeAs[AliasMetadata.RootScope] - } + val scopeInfo = rootScopeInfo("atom definition", atomDefn) def dataflowInfo() = atomDefn.unsafeGetMetadata( DataflowAnalysis, "No dataflow information associated with an atom." ) + def frameInfo() = atomDefn.unsafeGetMetadata( + FramePointerAnalysis, + "Method definition missing frame information." + ) val localScope = new LocalScope( None, () => scopeInfo().graph, () => scopeInfo().graph.rootScope, - dataflowInfo + dataflowInfo, + frameInfo ) val argFactory = new DefinitionArgumentProcessor( scope = localScope, - initialName = "Type " + tpDef.name + initialName = "Type " + tpDef.name.name ) val argDefs = new Array[ArgumentDefinition](atomDefn.arguments.size) @@ -303,6 +303,7 @@ class IrToTruffle( FramePointerAnalysis, "No frame pointer on an argument definition." ) + .asInstanceOf[FramePointer] val slotIdx = fp.frameSlotIdx() argDefs(idx) = arg val readArg = @@ -332,7 +333,8 @@ class IrToTruffle( () => scopeInfo().graph, () => scopeInfo().graph.rootScope, dataflowInfo, - atomDefn.name.name + atomDefn.name.name, + frameInfo ) val expressionNode = expressionProcessor.run(annotation.expression, true) @@ -369,19 +371,17 @@ class IrToTruffle( } methodDefs.foreach(methodDef => { - def scopeInfo() = { - methodDef - .unsafeGetMetadata( - AliasAnalysis, - s"Missing scope information for method " + - s"`${methodDef.typeName.map(_.name + ".").getOrElse("")}${methodDef.methodName.name}`." - ) - .unsafeAs[AliasMetadata.RootScope] - } + lazy val where = + s"`method ${methodDef.typeName.map(_.name + ".").getOrElse("")}${methodDef.methodName.name}`." + val scopeInfo = rootScopeInfo(where, methodDef) def dataflowInfo() = methodDef.unsafeGetMetadata( DataflowAnalysis, "Method definition missing dataflow information." ) + def frameInfo() = methodDef.unsafeGetMetadata( + FramePointerAnalysis, + "Method definition missing frame information." + ) @tailrec def getContext(tp: Expression): Option[String] = tp match { @@ -415,7 +415,8 @@ class IrToTruffle( () => scopeInfo().graph, () => scopeInfo().graph.rootScope, dataflowInfo, - fullMethodDefName + fullMethodDefName, + frameInfo ) scopeBuilder.registerMethod( @@ -546,17 +547,12 @@ class IrToTruffle( ) val scopeName = scopeElements.mkString(Constants.SCOPE_SEPARATOR) - def scopeInfo() = { - annotation - .unsafeGetMetadata( - AliasAnalysis, - s"Missing scope information for annotation " + - s"${annotation.name} of method " + - scopeElements.init - .mkString(Constants.SCOPE_SEPARATOR) - ) - .unsafeAs[AliasMetadata.RootScope] - } + + lazy val where = + s"annotation ${annotation.name} of method ${scopeElements.init + .mkString(Constants.SCOPE_SEPARATOR)}" + val scopeInfo = rootScopeInfo(where, annotation) + def dataflowInfo() = annotation.unsafeGetMetadata( DataflowAnalysis, "Missing dataflow information for annotation " + @@ -564,12 +560,17 @@ class IrToTruffle( scopeElements.init .mkString(Constants.SCOPE_SEPARATOR) ) + def frameInfo() = annotation.unsafeGetMetadata( + FramePointerAnalysis, + "Method definition missing frame information." + ) val expressionProcessor = new ExpressionProcessor( scopeName, () => scopeInfo().graph, () => scopeInfo().graph.rootScope, dataflowInfo, - methodDef.methodName.name + methodDef.methodName.name, + frameInfo ) val expressionNode = expressionProcessor.run(annotation.expression, true) @@ -599,9 +600,9 @@ class IrToTruffle( .annotations(annotations: _*) .argumentDefinitions(arguments: _*) if (methodDef.isPrivate) { - funcSchemaBldr.projectPrivate(); + funcSchemaBldr.projectPrivate() } - val funcSchema = funcSchemaBldr.build(); + val funcSchema = funcSchemaBldr.build() new RuntimeFunction( callTarget, null, @@ -700,7 +701,7 @@ class IrToTruffle( .newBuilder() .argumentDefinitions(bodyBuilder.args(): _*) if (methodDef.isPrivate) { - funcSchemaBldr.projectPrivate(); + funcSchemaBldr.projectPrivate() } val funcSchema = funcSchemaBldr.build() new RuntimeFunction( @@ -767,19 +768,18 @@ class IrToTruffle( // Register the conversion definitions in scope conversionDefs.foreach(methodDef => { - def scopeInfo() = { - methodDef - .unsafeGetMetadata( - AliasAnalysis, - s"Missing scope information for conversion " + - s"`${methodDef.typeName.map(_.name + ".").getOrElse("")}${methodDef.methodName.name}`." - ) - .unsafeAs[AliasMetadata.RootScope] - } + lazy val where = + s"conversion `${methodDef.typeName.map(_.name + ".").getOrElse("")}${methodDef.methodName.name}`." + val scopeInfo = rootScopeInfo(where, methodDef) + def dataflowInfo() = methodDef.unsafeGetMetadata( DataflowAnalysis, "Method definition missing dataflow information." ) + def frameInfo() = methodDef.unsafeGetMetadata( + FramePointerAnalysis, + "Method definition missing frame information." + ) val toOpt = methodDef.methodReference.typePointer match { @@ -795,7 +795,8 @@ class IrToTruffle( () => scopeInfo().graph, () => scopeInfo().graph.rootScope, dataflowInfo, - methodDef.methodName.name + methodDef.methodName.name, + frameInfo ) val function = methodDef.body match { @@ -1230,10 +1231,11 @@ class IrToTruffle( graph: () => AliasGraph, scope: () => AliasScope, dataflowInfo: () => DataflowAnalysis.Metadata, - initialName: String + initialName: String, + frameInfo: () => FramePointerAnalysis.Metadata = null ) = { this( - new LocalScope(None, graph, scope, dataflowInfo), + new LocalScope(None, graph, scope, dataflowInfo, frameInfo), scopeName, initialName ) @@ -1249,9 +1251,12 @@ class IrToTruffle( def createChild( name: String, scope: () => AliasScope, - initialName: String + initialName: String, + symbolsProvider: () => FrameVariableNames = null ): ExpressionProcessor = { - new ExpressionProcessor(this.scope.createChild(scope), name, initialName) + val childScope = + this.scope.createChild(scope, symbolsProvider = symbolsProvider) + new ExpressionProcessor(childScope, name, initialName) } // === Runner ============================================================= @@ -1304,7 +1309,7 @@ class IrToTruffle( ir.getMetadata(TypeSignatures) types.foreach { tpe => val checkNode = - extractAscribedType(tpe.comment.orNull, tpe.signature); + extractAscribedType(tpe.comment.orNull, tpe.signature) if (checkNode != null) { runtimeExpression = ReadArgumentCheckNode.wrap(runtimeExpression, checkNode) @@ -1334,19 +1339,19 @@ class IrToTruffle( */ private def processBlock(block: Expression.Block): RuntimeExpression = { if (block.suspended) { - def scopeInfo() = { - block - .unsafeGetMetadata( - AliasAnalysis, - "Missing scope information on block." - ) - .unsafeAs[AliasMetadata.ChildScope] - } + val scopeInfo = childScopeInfo("block", block) + def frameInfo() = block + .unsafeGetMetadata( + FramePointerAnalysis, + "Method definition missing frame information." + ) + .asInstanceOf[FrameVariableNames] val childFactory = this.createChild( "suspended-block", () => scopeInfo().scope, - "suspended " + currentVarName + "suspended " + currentVarName, + frameInfo ) val childScope = childFactory.scope @@ -1448,20 +1453,19 @@ class IrToTruffle( def processCaseBranch( branch: Case.Branch ): Either[BadPatternMatch, BranchNode] = { - def scopeInfo() = { - branch - .unsafeGetMetadata( - AliasAnalysis, - "No scope information on a case branch." - ) - .unsafeAs[AliasMetadata.ChildScope] - } - + val scopeInfo = childScopeInfo("case branch", branch) + def frameInfo() = branch + .unsafeGetMetadata( + FramePointerAnalysis, + "Method definition missing frame information." + ) + .asInstanceOf[FrameVariableNames] val childProcessor = this.createChild( "case_branch", () => scopeInfo().scope, - "case " + currentVarName + "case " + currentVarName, + frameInfo ) branch.pattern match { @@ -1607,7 +1611,7 @@ class IrToTruffle( BadPatternMatch.NonConstantPolyglotSymbol(symbol) ) } else { - val value = iop.readMember(polyClass, symbol); + val value = iop.readMember(polyClass, symbol) val ensoValue = HostValueToEnsoNode.getUncached().execute(value) Right(ensoValue) @@ -1834,6 +1838,7 @@ class IrToTruffle( FramePointerAnalysis, "Binding with missing frame pointer." ) + .asInstanceOf[FramePointer] currentVarName = binding.name.name val slotIdx = fp.frameSlotIdx() @@ -1853,17 +1858,19 @@ class IrToTruffle( function: Function, binding: Boolean ): RuntimeExpression = { - def scopeInfo() = { - function - .unsafeGetMetadata(AliasAnalysis, "No scope info on a function.") - .unsafeAs[AliasMetadata.ChildScope] - } + val scopeInfo = childScopeInfo("function", function) if (function.body.isInstanceOf[Function]) { throw new CompilerError( "Lambda found directly as function body. It looks like Lambda " + "Consolidation hasn't run." ) } + def frameInfo() = function + .unsafeGetMetadata( + FramePointerAnalysis, + "Method definition missing frame information." + ) + .asInstanceOf[FrameVariableNames] val scopeName = if (function.canBeTCO) { this.scopeName + "." + currentVarName @@ -1875,7 +1882,8 @@ class IrToTruffle( this.createChild( scopeName, () => scopeInfo().scope, - "case " + currentVarName + "case " + currentVarName, + frameInfo ) val fn = child.processFunctionBody( @@ -1898,8 +1906,8 @@ class IrToTruffle( case literalName: Name.Literal => val resolver = new RuntimeNameResolution() val fpMeta = literalName.passData.get(FramePointerAnalysis) match { - case Some(meta: FramePointerAnalysis.FramePointerMeta) => meta - case _ => null + case Some(meta: FramePointer) => meta + case _ => null } resolver.resolveName(literalName, fpMeta) case Name.MethodReference( @@ -1959,17 +1967,17 @@ class IrToTruffle( extends NameResolutionAlgorithm[ RuntimeExpression, FramePointer, - FramePointerAnalysis.FramePointerMeta + FramePointer ] { override protected def findLocalLink( - fpMeta: FramePointerAnalysis.FramePointerMeta + fpMeta: FramePointer ): Option[FramePointer] = { if (scope.flattenToParent && fpMeta.parentLevel() > 0) { Some( new FramePointer(fpMeta.parentLevel() - 1, fpMeta.frameSlotIdx()) ) } else { - Some(fpMeta.framePointer) + Some(fpMeta) } } @@ -2059,11 +2067,11 @@ class IrToTruffle( private def fileLocationFromSection(loc: IdentifiedLocation) = { val section = - source.createSection(loc.location().start(), loc.location().length()); + source.createSection(loc.location().start(), loc.location().length()) val locStr = "" + section.getStartLine() + ":" + section .getStartColumn() + "-" + section.getEndLine() + ":" + section .getEndColumn() - source.getName() + "[" + locStr + "]"; + source.getName() + "[" + locStr + "]" } /** Generates a runtime implementation for compile error nodes. @@ -2213,6 +2221,7 @@ class IrToTruffle( FramePointerAnalysis, "No frame pointer on an argument definition." ) + .asInstanceOf[FramePointer] val slotIdx = fp.frameSlotIdx() val readArg = ReadArgumentNode.build( @@ -2432,14 +2441,7 @@ class IrToTruffle( ): callable.argument.CallArgument = arg match { case CallArgument.Specified(name, value, _, _) => - def scopeInfo() = { - arg - .unsafeGetMetadata( - AliasAnalysis, - "No scope attached to a call argument." - ) - .unsafeAs[AliasMetadata.ChildScope] - } + val scopeInfo = childScopeInfo("call argument", arg) def valueHasSomeTypeCheck() = value.getMetadata(TypeSignatures).isDefined @@ -2452,7 +2454,17 @@ class IrToTruffle( } val childScope = if (shouldCreateClosureRootNode) { - scope.createChild(() => scopeInfo().scope) + def frameInfo() = arg + .unsafeGetMetadata( + FramePointerAnalysis, + "Method definition missing frame information." + ) + .asInstanceOf[FrameVariableNames] + + scope.createChild( + () => scopeInfo().scope, + symbolsProvider = frameInfo + ) } else { // Note [Scope Flattening] scope.createChild(() => scopeInfo().scope, flattenToParent = true) @@ -2618,4 +2630,43 @@ class IrToTruffle( private def scopeAssociatedType = scopeBuilder.asModuleScope().getAssociatedType + + private def rootScopeInfo( + where: => String, + ir: IR + ): () => AliasMetadata.RootScope = { + def readScopeInfo() = { + val raw = + ir.unsafeGetMetadata(AliasAnalysis, s"No root scope for ${where}.") + val scope = raw.unsafeAs[AliasMetadata.RootScope] + + val log = context.getLogger() + if (log.isLoggable(Level.FINEST)) { + val allDefs = scope.graph.rootScope.allDefinitions + log.log(Level.FINEST, s"Scope for ${where} loaded with {0}", allDefs) + } + scope + } + readScopeInfo + } + + private def childScopeInfo( + where: => String, + ir: IR + ): () => AliasMetadata.ChildScope = { + def readScopeInfo() = { + val raw = + ir.unsafeGetMetadata(AliasAnalysis, s"No root scope for ${where}.") + val scope = raw.unsafeAs[AliasMetadata.ChildScope] + + val log = context.getLogger() + if (log.isLoggable(Level.FINEST)) { + val allDefs = scope.graph.rootScope.allDefinitions + log.log(Level.FINEST, s"Scope for ${where} loaded with {0}", allDefs) + } + scope + } + readScopeInfo + } + }