From 7cced6a9de5308490ca22b27bb0bc064a245cc6f Mon Sep 17 00:00:00 2001 From: Ara Adkins Date: Tue, 31 Aug 2021 15:50:33 +0100 Subject: [PATCH] Make the `BindingsMap` able to break links (#1980) --- .../launcher/cli/LauncherApplication.scala | 2 +- .../src/main/scala/org/enso/runner/Main.scala | 2 +- .../org/enso/compiler/PackageRepository.scala | 39 +- .../enso/compiler/codegen/IrToTruffle.scala | 50 ++- .../org/enso/compiler/data/BindingsMap.scala | 334 ++++++++++++++++-- .../pass/analyse/BindingAnalysis.scala | 3 +- .../pass/resolve/ModuleThisToHere.scala | 7 +- .../pass/resolve/UppercaseNames.scala | 4 +- .../pass/resolve/VectorLiterals.scala | 32 +- .../compiler/phase/ExportsResolution.scala | 44 ++- .../enso/compiler/phase/ImportResolver.scala | 13 +- .../enso/compiler/phase/StubIrBuilder.scala | 17 +- .../pass/analyse/BindingAnalysisTest.scala | 6 +- .../pass/resolve/MethodDefinitionsTest.scala | 28 +- .../test/pass/resolve/PatternsTest.scala | 8 +- .../pass/resolve/UppercaseNamesTest.scala | 19 +- .../test/instrument/RuntimeStdlibTest.scala | 3 +- 17 files changed, 509 insertions(+), 102 deletions(-) diff --git a/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherApplication.scala b/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherApplication.scala index 7df989add2..8482ce4990 100644 --- a/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherApplication.scala +++ b/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherApplication.scala @@ -124,7 +124,7 @@ object LauncherApplication { "(error | warning | info | debug | trace)", "Sets logging verbosity for the engine. Defaults to info." ) - .withDefault(LogLevel.Info) + .withDefault(LogLevel.Warning) } private def runCommand: Command[Config => Int] = diff --git a/engine/runner/src/main/scala/org/enso/runner/Main.scala b/engine/runner/src/main/scala/org/enso/runner/Main.scala index 92fb7fd608..8b9a61e5ca 100644 --- a/engine/runner/src/main/scala/org/enso/runner/Main.scala +++ b/engine/runner/src/main/scala/org/enso/runner/Main.scala @@ -649,7 +649,7 @@ object Main { /** Default log level to use if the LOG_LEVEL option is not provided. */ - val defaultLogLevel: LogLevel = LogLevel.Info + val defaultLogLevel: LogLevel = LogLevel.Warning /** Main entry point for the CLI program. * diff --git a/engine/runtime/src/main/scala/org/enso/compiler/PackageRepository.scala b/engine/runtime/src/main/scala/org/enso/compiler/PackageRepository.scala index 480d942cc4..4c946f690e 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/PackageRepository.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/PackageRepository.scala @@ -35,10 +35,21 @@ trait PackageRepository { ): Either[PackageRepository.Error, Unit] /** Get a sequence of currently loaded packages. */ - def getLoadedPackages(): Seq[Package[TruffleFile]] + def getLoadedPackages: Seq[Package[TruffleFile]] /** Get a sequence of currently loaded modules. */ - def getLoadedModules(): Seq[Module] + def getLoadedModules: Seq[Module] + + /** Get the mapping from qualified module names (equivalent to + * [[QualifiedName.toString]]) to modules. + * + * This map may be updated concurrently. + */ + def getModuleMap: PackageRepository.ModuleMap + + /** Gets a frozen form of the module map that cannot be updated concurrently. + */ + def freezeModuleMap: PackageRepository.FrozenModuleMap /** Get a loaded module by its qualified name. */ def getLoadedModule(qualifiedName: String): Option[Module] @@ -63,6 +74,9 @@ trait PackageRepository { object PackageRepository { + type ModuleMap = collection.concurrent.Map[String, Module] + type FrozenModuleMap = Map[String, Module] + /** A trait representing errors reported by this system */ sealed trait Error @@ -130,10 +144,23 @@ object PackageRepository { collection.concurrent.TrieMap(builtinsName -> None) } - /** The mapping containing loaded modules. */ + /** The mapping containing loaded modules. + * + * We use [[String]] as the key as we often index into this map based on + * qualified names that come from interop (via + * [[org.enso.interpreter.runtime.scope.TopLevelScope]]). These arrive as + * Strings, and constantly converting them into [[QualifiedName]]s would + * add more overhead than is probably necessary. + */ val loadedModules: collection.concurrent.Map[String, Module] = collection.concurrent.TrieMap(Builtins.MODULE_NAME -> builtins.getModule) + /** @inheritdoc */ + override def getModuleMap: ModuleMap = loadedModules + + /** @inheritdoc */ + override def freezeModuleMap: FrozenModuleMap = loadedModules.toMap + /** @inheritdoc */ override def registerMainProjectPackage( libraryName: LibraryName, @@ -235,11 +262,11 @@ object PackageRepository { } /** @inheritdoc */ - override def getLoadedModules(): Seq[Module] = + override def getLoadedModules: Seq[Module] = loadedModules.values.toSeq /** @inheritdoc */ - override def getLoadedPackages(): Seq[Package[TruffleFile]] = + override def getLoadedPackages: Seq[Package[TruffleFile]] = loadedPackages.values.toSeq.flatten /** @inheritdoc */ @@ -352,7 +379,7 @@ object PackageRepository { languageHome = homeManager, edition = edition, preferLocalLibraries = - projectPackage.map(_.config.preferLocalLibraries).getOrElse(false) + projectPackage.exists(_.config.preferLocalLibraries) ) new Default( resolvingLibraryProvider, diff --git a/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala b/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala index a61370766e..ece4b00e37 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala @@ -150,7 +150,9 @@ class IrToTruffle( "No binding analysis at the point of codegen." ) .resolvedExports - .foreach { exp => moduleScope.addExport(exp.module.getScope) } + .foreach { exp => + moduleScope.addExport(exp.module.unsafeAsModule().getScope) + } val imports = module.imports val atomDefs = module.bindings.collect { case atom: IR.Module.Scope.Definition.Atom => atom @@ -230,9 +232,13 @@ class IrToTruffle( .map { res => res.target match { case BindingsMap.ResolvedModule(module) => - module.getScope.getAssociatedType + module.unsafeAsModule().getScope.getAssociatedType case BindingsMap.ResolvedConstructor(definitionModule, cons) => - definitionModule.getScope.getConstructors.get(cons.name) + definitionModule + .unsafeAsModule() + .getScope + .getConstructors + .get(cons.name) case BindingsMap.ResolvedPolyglotSymbol(_, _) => throw new CompilerError( "Impossible polyglot symbol, should be caught by MethodDefinitions pass." @@ -379,11 +385,14 @@ class IrToTruffle( ) bindingsMap.exportedSymbols.foreach { case (name, List(resolution)) => - if (resolution.module != moduleScope.getModule) { + if (resolution.module.unsafeAsModule() != moduleScope.getModule) { resolution match { case BindingsMap.ResolvedConstructor(definitionModule, cons) => - val runtimeCons = - definitionModule.getScope.getConstructors.get(cons.name) + val runtimeCons = definitionModule + .unsafeAsModule() + .getScope + .getConstructors + .get(cons.name) val fun = mkConsGetter(runtimeCons) moduleScope.registerMethod( moduleScope.getAssociatedType, @@ -392,7 +401,7 @@ class IrToTruffle( ) case BindingsMap.ResolvedModule(module) => val runtimeCons = - module.getScope.getAssociatedType + module.unsafeAsModule().getScope.getAssociatedType val fun = mkConsGetter(runtimeCons) moduleScope.registerMethod( moduleScope.getAssociatedType, @@ -400,8 +409,9 @@ class IrToTruffle( fun ) case BindingsMap.ResolvedMethod(module, method) => - val fun = module.getScope.getMethods - .get(module.getScope.getAssociatedType) + val actualModule = module.unsafeAsModule() + val fun = actualModule.getScope.getMethods + .get(actualModule.getScope.getAssociatedType) .get(method.name) moduleScope.registerMethod( moduleScope.getAssociatedType, @@ -667,13 +677,15 @@ class IrToTruffle( case Some( BindingsMap.Resolution(BindingsMap.ResolvedModule(mod)) ) => - Right(mod.getScope.getAssociatedType) + Right(mod.unsafeAsModule().getScope.getAssociatedType) case Some( BindingsMap.Resolution( BindingsMap.ResolvedConstructor(mod, cons) ) ) => - Right(mod.getScope.getConstructors.get(cons.name)) + Right( + mod.unsafeAsModule().getScope.getConstructors.get(cons.name) + ) case Some( BindingsMap.Resolution( BindingsMap.ResolvedPolyglotSymbol(_, _) @@ -867,13 +879,23 @@ class IrToTruffle( resolution match { case BindingsMap.ResolvedConstructor(definitionModule, cons) => ConstructorNode.build( - definitionModule.getScope.getConstructors.get(cons.name) + definitionModule + .unsafeAsModule() + .getScope + .getConstructors + .get(cons.name) ) case BindingsMap.ResolvedModule(module) => - ConstructorNode.build(module.getScope.getAssociatedType) + ConstructorNode.build( + module.unsafeAsModule().getScope.getAssociatedType + ) case BindingsMap.ResolvedPolyglotSymbol(module, symbol) => ConstantObjectNode.build( - module.getScope.getPolyglotSymbols.get(symbol.name) + module + .unsafeAsModule() + .getScope + .getPolyglotSymbols + .get(symbol.name) ) case BindingsMap.ResolvedMethod(_, _) => throw new CompilerError( diff --git a/engine/runtime/src/main/scala/org/enso/compiler/data/BindingsMap.scala b/engine/runtime/src/main/scala/org/enso/compiler/data/BindingsMap.scala index f11df622ff..91837c6954 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/data/BindingsMap.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/data/BindingsMap.scala @@ -1,9 +1,14 @@ package org.enso.compiler.data +import org.enso.compiler.PackageRepository +import org.enso.compiler.PackageRepository.ModuleMap import org.enso.compiler.core.IR +import org.enso.compiler.data.BindingsMap.ModuleReference +import org.enso.compiler.exception.CompilerError import org.enso.compiler.pass.IRPass import org.enso.compiler.pass.analyse.BindingAnalysis import org.enso.interpreter.runtime.Module +import org.enso.pkg.QualifiedName /** A utility structure for resolving symbols in a given module. * @@ -16,7 +21,7 @@ case class BindingsMap( types: List[BindingsMap.Cons], polyglotSymbols: List[BindingsMap.PolyglotSymbol], moduleMethods: List[BindingsMap.ModuleMethod], - currentModule: Module + currentModule: ModuleReference ) extends IRPass.Metadata { import BindingsMap._ @@ -28,10 +33,81 @@ case class BindingsMap( */ var resolvedImports: List[ResolvedImport] = List() + /** Modules exported by [[currentModule]]. + */ var resolvedExports: List[ExportedModule] = List() + /** Symbols exported by [[currentModule]]. + */ var exportedSymbols: Map[String, List[ResolvedName]] = Map() + /** Convert this [[BindingsMap]] instance to use abstract module references. + * + * @return `this` with module references converted to abstract + */ + def toAbstract: BindingsMap = { + val copy = this.copy(currentModule = currentModule.toAbstract) + copy.resolvedImports = this.resolvedImports.map(_.toAbstract) + copy.resolvedExports = this.resolvedExports.map(_.toAbstract) + copy.exportedSymbols = this.exportedSymbols.map { case (key, value) => + key -> value.map(name => name.toAbstract) + } + + copy + } + + /** Convert this [[BindingsMap]] instance to use concrete module references. + * + * @param moduleMap the mapping from qualified module names to module + * instances + * @return `this` with module references converted to concrete + */ + def toConcrete(moduleMap: ModuleMap): Option[BindingsMap] = { + val newMap = this.currentModule.toConcrete(moduleMap).map { module => + this.copy(currentModule = module) + } + + val withImports: Option[BindingsMap] = newMap.flatMap { bindings => + val newImports = this.resolvedImports.map(_.toConcrete(moduleMap)) + if (newImports.exists(_.isEmpty)) { + None + } else { + bindings.resolvedImports = newImports.map(_.get) + Some(bindings) + } + } + + val withExports: Option[BindingsMap] = withImports.flatMap { bindings => + val newExports = this.resolvedExports.map(_.toConcrete(moduleMap)) + if (newExports.exists(_.isEmpty)) { + None + } else { + bindings.resolvedExports = newExports.map(_.get) + Some(bindings) + } + } + + val withSymbols: Option[BindingsMap] = withExports.flatMap { bindings => + val newSymbols = this.exportedSymbols.map { case (key, value) => + val newValue = value.map(_.toConcrete(moduleMap)) + if (newValue.exists(_.isEmpty)) { + key -> None + } else { + key -> Some(newValue.map(_.get)) + } + } + + if (newSymbols.exists { case (_, v) => v.isEmpty }) { + None + } else { + bindings.exportedSymbols = newSymbols.map { case (k, v) => k -> v.get } + Some(bindings) + } + } + + withSymbols + } + private def findConstructorCandidates( name: String ): List[ResolvedConstructor] = { @@ -79,12 +155,19 @@ case class BindingsMap( resolvedImports .flatMap { imp => if (imp.importDef.allowsAccess(name)) { - imp.module.getIr - .unsafeGetMetadata( - BindingAnalysis, - "Wrong pass ordering. Running resolution on an unparsed module." - ) - .findExportedSymbolsFor(name) + imp.module match { + case ModuleReference.Concrete(module) => + module.getIr + .unsafeGetMetadata( + BindingAnalysis, + "Wrong pass ordering. Running resolution on an unparsed module." + ) + .findExportedSymbolsFor(name) + case ModuleReference.Abstract(name) => + throw new CompilerError( + s"Cannot find export candidates for abstract module reference $name." + ) + } } else { List() } } } @@ -99,17 +182,24 @@ case class BindingsMap( } } - private def getBindingsFrom(module: Module): BindingsMap = { - module.getIr.unsafeGetMetadata( - BindingAnalysis, - "imported module has no binding map info" - ) + private def getBindingsFrom(module: ModuleReference): BindingsMap = { + module match { + case ModuleReference.Concrete(module) => + module.getIr.unsafeGetMetadata( + BindingAnalysis, + "imported module has no binding map info" + ) + case ModuleReference.Abstract(_) => + throw new CompilerError( + "Bindings cannot be obtained from an abstract module reference." + ) + } } /** Resolves a name in the context of current module. * * @param name the name to resolve. - * @return a resolution for [[name]] or an error, if the name could not be + * @return a resolution for `name` or an error, if the name could not be * resolved. */ def resolveUppercaseName( @@ -430,10 +520,28 @@ object BindingsMap { * @param symbols any symbol restrictions connected to the export. */ case class ExportedModule( - module: Module, + module: ModuleReference, exportedAs: Option[String], symbols: SymbolRestriction - ) + ) { + + /** Convert the internal [[ModuleReference]] to an abstract reference. + * + * @return `this` with its module reference made abstract + */ + def toAbstract: ExportedModule = { + this.copy(module = module.toAbstract) + } + + /** Convert the internal [[ModuleReference]] to a concrete reference. + * + * @param moduleMap the mapping from qualified names to modules + * @return `this` with its module reference made concrete + */ + def toConcrete(moduleMap: ModuleMap): Option[ExportedModule] = { + module.toConcrete(moduleMap).map(x => this.copy(module = x)) + } + } /** A representation of a resolved import statement. * @@ -444,8 +552,26 @@ object BindingsMap { case class ResolvedImport( importDef: IR.Module.Scope.Import.Module, exports: Option[IR.Module.Scope.Export.Module], - module: Module - ) + module: ModuleReference + ) { + + /** Convert the internal [[ModuleReference]] to an abstract reference. + * + * @return `this` with its module reference made abstract + */ + def toAbstract: ResolvedImport = { + this.copy(module = module.toAbstract) + } + + /** Convert the internal [[ModuleReference]] to a concrete reference. + * + * @param moduleMap the mapping from qualified names to modules + * @return `this` with its module reference made concrete + */ + def toConcrete(moduleMap: ModuleMap): Option[ResolvedImport] = { + module.toConcrete(moduleMap).map(x => this.copy(module = x)) + } + } /** A representation of a constructor. * @@ -469,7 +595,20 @@ object BindingsMap { /** A result of successful name resolution. */ sealed trait ResolvedName { - def module: Module + def module: ModuleReference + + /** Convert the resolved name to abstract form. + * + * @return `this`, converted to abstract form + */ + def toAbstract: ResolvedName + + /** Convert the resolved name to concrete form. + * + * @param moduleMap the mapping from qualified names to modules + * @return `this`, converted to concrete form + */ + def toConcrete(moduleMap: ModuleMap): Option[ResolvedName] } /** A representation of a name being resolved to a constructor. @@ -477,28 +616,83 @@ object BindingsMap { * @param module the module the constructor is defined in. * @param cons a representation of the constructor. */ - case class ResolvedConstructor(module: Module, cons: Cons) - extends ResolvedName + case class ResolvedConstructor(module: ModuleReference, cons: Cons) + extends ResolvedName { + + /** @inheritdoc */ + override def toAbstract: ResolvedConstructor = { + this.copy(module = module.toAbstract) + } + + /** @inheritdoc */ + override def toConcrete( + moduleMap: ModuleMap + ): Option[ResolvedConstructor] = { + module.toConcrete(moduleMap).map(module => this.copy(module = module)) + } + } /** A representation of a name being resolved to a module. * * @param module the module the name resolved to. */ - case class ResolvedModule(module: Module) extends ResolvedName + case class ResolvedModule(module: ModuleReference) extends ResolvedName { + + /** @inheritdoc */ + override def toAbstract: ResolvedModule = { + this.copy(module = module.toAbstract) + } + + /** @inheritdoc */ + override def toConcrete( + moduleMap: ModuleMap + ): Option[ResolvedModule] = { + module.toConcrete(moduleMap).map(module => this.copy(module = module)) + } + } /** A representation of a name being resolved to a method call. + * * @param module the module defining the method. * @param method the method representation. */ - case class ResolvedMethod(module: Module, method: ModuleMethod) - extends ResolvedName + case class ResolvedMethod(module: ModuleReference, method: ModuleMethod) + extends ResolvedName { + + /** @inheritdoc */ + override def toAbstract: ResolvedMethod = { + this.copy(module = module.toAbstract) + } + + /** @inheritdoc */ + override def toConcrete( + moduleMap: ModuleMap + ): Option[ResolvedMethod] = { + module.toConcrete(moduleMap).map(module => this.copy(module = module)) + } + } /** A representation of a name being resolved to a polyglot symbol. * * @param symbol the imported symbol name. */ - case class ResolvedPolyglotSymbol(module: Module, symbol: PolyglotSymbol) - extends ResolvedName + case class ResolvedPolyglotSymbol( + module: ModuleReference, + symbol: PolyglotSymbol + ) extends ResolvedName { + + /** @inheritdoc */ + override def toAbstract: ResolvedPolyglotSymbol = { + this.copy(module = module.toAbstract) + } + + /** @inheritdoc */ + override def toConcrete( + moduleMap: ModuleMap + ): Option[ResolvedPolyglotSymbol] = { + module.toConcrete(moduleMap).map(module => this.copy(module = module)) + } + } /** A representation of an error during name resolution. */ @@ -532,4 +726,94 @@ object BindingsMap { */ override def duplicate(): Option[IRPass.Metadata] = Some(this) } + + /** A reference to a module. + */ + sealed trait ModuleReference { + + /** @return the qualified name of the module + */ + def getName: QualifiedName + + /** Convert `this` into a concrete module reference. + * + * @param moduleMap the mapping from qualified names to concrete modules + * @return the concrete module for this reference, if possible + */ + def toConcrete( + moduleMap: PackageRepository.ModuleMap + ): Option[ModuleReference.Concrete] + + /** Convert `this` into an abstract module reference. + * + * @return the abstract reference to the module represented by `this` + */ + def toAbstract: ModuleReference.Abstract + + /** Unsafely coerces the module reference to a concrete one. + * + * @param message the message for if the coercion fails + * @return the concrete version of this reference + */ + @throws[CompilerError] + def unsafeAsModule(message: String = ""): Module + } + object ModuleReference { + + /** A module reference that points to a concrete [[Module]] object. + * + * @param module the module being referenced + */ + case class Concrete(module: Module) extends ModuleReference { + + /** @inheritdoc */ + override def getName: QualifiedName = module.getName + + /** Converts `this` into a concrete module reference (a no-op). + * + * @param moduleMap the mapping from qualified names to concrete modules + * @return the concrete module for this reference, if possible + */ + override def toConcrete(moduleMap: ModuleMap): Option[Concrete] = + Some(this) + + /** @inheritdoc */ + override def toAbstract: Abstract = + ModuleReference.Abstract(module.getName) + + /** @inheritdoc */ + override def unsafeAsModule(message: String = ""): Module = module + } + + /** A module reference that refers to a module by qualified name, without an + * explicit link to the target. + * + * @param name the qualified name (including namespace) of the module + * being referenced + */ + case class Abstract(name: QualifiedName) extends ModuleReference { + + /** @inheritdoc */ + override def getName: QualifiedName = name + + /** @inheritdoc */ + override def toConcrete(moduleMap: ModuleMap): Option[Concrete] = { + moduleMap.get(name.toString).map(Concrete) + } + + /** Convert `this` into an abstract module reference (a no-op). + * + * @return the abstract reference to the module represented by `this` + */ + override def toAbstract: Abstract = this + + /** @inheritdoc */ + override def unsafeAsModule(message: String = ""): Module = { + val rest = if (message.isEmpty) "." else s": $message" + val errMsg = s"Could not get concrete module from abstract module $name$rest" + + throw new CompilerError(errMsg) + } + } + } } diff --git a/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/BindingAnalysis.scala b/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/BindingAnalysis.scala index 09cf27612f..36ee12f9db 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/BindingAnalysis.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/BindingAnalysis.scala @@ -4,6 +4,7 @@ import org.enso.compiler.context.{InlineContext, ModuleContext} import org.enso.compiler.core.IR import org.enso.compiler.core.ir.MetadataStorage.ToPair import org.enso.compiler.data.BindingsMap +import org.enso.compiler.data.BindingsMap.ModuleReference import org.enso.compiler.pass.IRPass import org.enso.compiler.pass.desugar.{ ComplexType, @@ -81,7 +82,7 @@ case object BindingAnalysis extends IRPass { definedConstructors, importedPolyglot, methodsWithAutogen, - moduleContext.module + ModuleReference.Concrete(moduleContext.module) ) ) } diff --git a/engine/runtime/src/main/scala/org/enso/compiler/pass/resolve/ModuleThisToHere.scala b/engine/runtime/src/main/scala/org/enso/compiler/pass/resolve/ModuleThisToHere.scala index c3594639ef..8b213d5c9d 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/pass/resolve/ModuleThisToHere.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/pass/resolve/ModuleThisToHere.scala @@ -3,7 +3,7 @@ package org.enso.compiler.pass.resolve import org.enso.compiler.context.{InlineContext, ModuleContext} import org.enso.compiler.core.IR import org.enso.compiler.data.BindingsMap -import org.enso.compiler.data.BindingsMap.ResolvedModule +import org.enso.compiler.data.BindingsMap.{ModuleReference, ResolvedModule} import org.enso.compiler.exception.CompilerError import org.enso.compiler.pass.IRPass import org.enso.compiler.pass.analyse.AliasAnalysis @@ -46,8 +46,9 @@ case object ModuleThisToHere extends IRPass { ir: IR.Module, moduleContext: ModuleContext ): IR.Module = { - val localResolution = - BindingsMap.Resolution(ResolvedModule(moduleContext.module)) + val localResolution = BindingsMap.Resolution( + ResolvedModule(ModuleReference.Concrete(moduleContext.module)) + ) val newBindings = ir.bindings.map { case m: IR.Module.Scope.Definition.Method => if ( diff --git a/engine/runtime/src/main/scala/org/enso/compiler/pass/resolve/UppercaseNames.scala b/engine/runtime/src/main/scala/org/enso/compiler/pass/resolve/UppercaseNames.scala index f92114f1e6..4de7d88d0e 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/pass/resolve/UppercaseNames.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/pass/resolve/UppercaseNames.scala @@ -251,7 +251,9 @@ case object UppercaseNames extends IRPass { ): Option[BindingsMap.ResolvedConstructor] = thisResolution.target match { case BindingsMap.ResolvedModule(module) => - val resolution = module.getIr + val resolution = module + .unsafeAsModule() + .getIr .unsafeGetMetadata( BindingAnalysis, "Imported module without bindings analysis results" diff --git a/engine/runtime/src/main/scala/org/enso/compiler/pass/resolve/VectorLiterals.scala b/engine/runtime/src/main/scala/org/enso/compiler/pass/resolve/VectorLiterals.scala index 6a0f982f9e..df6c50d170 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/pass/resolve/VectorLiterals.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/pass/resolve/VectorLiterals.scala @@ -4,8 +4,10 @@ import org.enso.compiler.context.{InlineContext, ModuleContext} import org.enso.compiler.core.IR import org.enso.compiler.core.ir.MetadataStorage.ToPair import org.enso.compiler.data.BindingsMap +import org.enso.compiler.data.BindingsMap.ModuleReference import org.enso.compiler.pass.IRPass import org.enso.compiler.pass.analyse.BindingAnalysis +import org.enso.interpreter.runtime.Module case object VectorLiterals extends IRPass { @@ -21,6 +23,9 @@ case object VectorLiterals extends IRPass { /** The passes that are invalidated by running this pass. */ override val invalidatedPasses: Seq[IRPass] = Seq() + /** The name of the module that contains the Enso stdlib vector definition. */ + val vectorModuleName: String = "Standard.Base.Data.Vector" + /** Executes the pass on the provided `ir`, and returns a possibly transformed * or annotated version of `ir`. * @@ -64,17 +69,17 @@ case object VectorLiterals extends IRPass { } private def vectorCons(bindings: BindingsMap): IR.Expression = { - val module = bindings.resolvedImports - .flatMap(imp => - imp.module :: imp.module.getIr - .unsafeGetMetadata( - BindingAnalysis, - "no binding analyis on an imported module" - ) - .resolvedExports - .map(_.module) - ) - .find(_.getName.toString == "Standard.Base.Data.Vector") + val modules: List[Module] = bindings.resolvedImports.flatMap { imp => + val module = imp.module.unsafeAsModule() + module :: module.getIr + .unsafeGetMetadata( + BindingAnalysis, + "no binding analysis on an imported module" + ) + .resolvedExports + .map { export => export.module.unsafeAsModule() } + } + val module = modules.find(_.getName.toString == vectorModuleName) val name = IR.Name.Literal( "", isReferent = true, @@ -86,7 +91,10 @@ case object VectorLiterals extends IRPass { val withRes = name.updateMetadata( UppercaseNames -->> BindingsMap.Resolution( BindingsMap - .ResolvedConstructor(module, BindingsMap.Cons("Vector", 1)) + .ResolvedConstructor( + ModuleReference.Concrete(module), + BindingsMap.Cons("Vector", 1) + ) ) ) withRes diff --git a/engine/runtime/src/main/scala/org/enso/compiler/phase/ExportsResolution.scala b/engine/runtime/src/main/scala/org/enso/compiler/phase/ExportsResolution.scala index 6d2b864ebc..104dd9aa3c 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/phase/ExportsResolution.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/phase/ExportsResolution.scala @@ -3,6 +3,7 @@ package org.enso.compiler.phase import org.enso.compiler.data.BindingsMap import org.enso.compiler.data.BindingsMap.{ ExportedModule, + ModuleReference, ResolvedConstructor, ResolvedMethod, ResolvedModule, @@ -53,7 +54,7 @@ class ExportsResolution { val node = nodes(module) node.exports = exports.map { case ExportedModule(mod, rename, restriction) => - Edge(node, restriction, rename, nodes(mod)) + Edge(node, restriction, rename, nodes(mod.unsafeAsModule())) } node.exports.foreach { edge => edge.exportee.exportedBy ::= edge } } @@ -122,12 +123,16 @@ class ExportsResolution { nodes.foreach { node => val explicitlyExported = node.exports.map(edge => - ExportedModule(edge.exportee.module, edge.exportsAs, edge.symbols) + ExportedModule( + ModuleReference.Concrete(edge.exportee.module), + edge.exportsAs, + edge.symbols + ) ) val transitivelyExported: List[ExportedModule] = explicitlyExported.flatMap { case ExportedModule(module, _, restriction) => - exports(module).map { + exports(module.unsafeAsModule()).map { case ExportedModule(export, _, parentRestriction) => ExportedModule( export, @@ -160,27 +165,36 @@ class ExportsResolution { private def resolveExportedSymbols(modules: List[Module]): Unit = { modules.foreach { module => val bindings = getBindings(module) - val ownMethods = bindings.moduleMethods.map(method => - (method.name.toLowerCase, List(ResolvedMethod(module, method))) - ) - val ownConstructors = bindings.types.map(tp => - (tp.name.toLowerCase, List(ResolvedConstructor(module, tp))) - ) - val ownPolyglotBindings = bindings.polyglotSymbols.map(poly => - (poly.name.toLowerCase, List(ResolvedPolyglotSymbol(module, poly))) - ) + val ownMethods = bindings.moduleMethods.map { method => + val name = method.name.toLowerCase + val syms = + List(ResolvedMethod(ModuleReference.Concrete(module), method)) + (name, syms) + } + val ownConstructors = bindings.types.map { tp => + val name = tp.name.toLowerCase + val types = + List(ResolvedConstructor(ModuleReference.Concrete(module), tp)) + (name, types) + } + val ownPolyglotBindings = bindings.polyglotSymbols.map { poly => + val name = poly.name.toLowerCase + val syms = + List(ResolvedPolyglotSymbol(ModuleReference.Concrete(module), poly)) + (name, syms) + } val exportedModules = bindings.resolvedExports.collect { case ExportedModule(mod, Some(name), _) => (name.toLowerCase, List(ResolvedModule(mod))) } val reExportedSymbols = bindings.resolvedExports.flatMap { export => - getBindings(export.module).exportedSymbols.toList.flatMap { - case (sym, resolutions) => + getBindings(export.module.unsafeAsModule()).exportedSymbols.toList + .flatMap { case (sym, resolutions) => val allowedResolutions = resolutions.filter(res => export.symbols.canAccess(sym, res)) if (allowedResolutions.isEmpty) None else Some((sym, allowedResolutions)) - } + } } bindings.exportedSymbols = List( ownMethods, diff --git a/engine/runtime/src/main/scala/org/enso/compiler/phase/ImportResolver.scala b/engine/runtime/src/main/scala/org/enso/compiler/phase/ImportResolver.scala index fd32aa00f0..c441bd7107 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/phase/ImportResolver.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/phase/ImportResolver.scala @@ -3,6 +3,7 @@ package org.enso.compiler.phase import org.enso.compiler.Compiler import org.enso.compiler.core.IR import org.enso.compiler.data.BindingsMap +import org.enso.compiler.data.BindingsMap.ModuleReference import org.enso.compiler.exception.CompilerError import org.enso.compiler.pass.analyse.BindingAnalysis import org.enso.editions.LibraryName @@ -78,7 +79,13 @@ class ImportResolver(compiler: Compiler) { case Some(module) => ( imp, - Some(BindingsMap.ResolvedImport(imp, exp, module)) + Some( + BindingsMap.ResolvedImport( + imp, + exp, + ModuleReference.Concrete(module) + ) + ) ) case None => ( @@ -112,7 +119,9 @@ class ImportResolver(compiler: Compiler) { } // continue with updated stack go( - stack.pushAll(currentLocal.resolvedImports.map(_.module)), + stack.pushAll( + currentLocal.resolvedImports.map(_.module.unsafeAsModule()) + ), seen += current ) } diff --git a/engine/runtime/src/main/scala/org/enso/compiler/phase/StubIrBuilder.scala b/engine/runtime/src/main/scala/org/enso/compiler/phase/StubIrBuilder.scala index f5652fb26d..6b65f53604 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/phase/StubIrBuilder.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/phase/StubIrBuilder.scala @@ -3,7 +3,11 @@ package org.enso.compiler.phase import org.enso.compiler.core.IR import org.enso.compiler.core.ir.MetadataStorage._ import org.enso.compiler.data.BindingsMap -import org.enso.compiler.data.BindingsMap.{ResolvedConstructor, ResolvedMethod} +import org.enso.compiler.data.BindingsMap.{ + ModuleReference, + ResolvedConstructor, + ResolvedMethod +} import org.enso.compiler.pass.analyse.BindingAnalysis import org.enso.interpreter.runtime.Module @@ -39,13 +43,18 @@ object StubIrBuilder { val polyglot = scope.getPolyglotSymbols.asScala.keys.toList .map(BindingsMap.PolyglotSymbol) val exportedBindings = definedConstructors.map(c => - (c.name.toLowerCase, List(ResolvedConstructor(module, c))) - ) ++ moduleMethods.map(m => (m.name, List(ResolvedMethod(module, m)))) + ( + c.name.toLowerCase, + List(ResolvedConstructor(ModuleReference.Concrete(module), c)) + ) + ) ++ moduleMethods.map(m => + (m.name, List(ResolvedMethod(ModuleReference.Concrete(module), m))) + ) val meta = BindingsMap( definedConstructors, polyglot, moduleMethods, - module + ModuleReference.Concrete(module) ) meta.exportedSymbols = exportedBindings.toMap ir.updateMetadata(BindingAnalysis -->> meta) diff --git a/engine/runtime/src/test/scala/org/enso/compiler/test/pass/analyse/BindingAnalysisTest.scala b/engine/runtime/src/test/scala/org/enso/compiler/test/pass/analyse/BindingAnalysisTest.scala index 559aac84a3..8af54261fc 100644 --- a/engine/runtime/src/test/scala/org/enso/compiler/test/pass/analyse/BindingAnalysisTest.scala +++ b/engine/runtime/src/test/scala/org/enso/compiler/test/pass/analyse/BindingAnalysisTest.scala @@ -3,7 +3,7 @@ package org.enso.compiler.test.pass.analyse import org.enso.compiler.Passes import org.enso.compiler.context.{FreshNameSupply, ModuleContext} import org.enso.compiler.core.IR -import org.enso.compiler.data.BindingsMap.{Cons, ModuleMethod, PolyglotSymbol} +import org.enso.compiler.data.BindingsMap.{Cons, ModuleMethod, ModuleReference, PolyglotSymbol} import org.enso.compiler.pass.analyse.BindingAnalysis import org.enso.compiler.pass.{PassConfiguration, PassGroup, PassManager} import org.enso.compiler.test.CompilerTest @@ -37,7 +37,7 @@ class BindingAnalysisTest extends CompilerTest { * @param context the module context in which analysis takes place * @return [[ir]], with tail call analysis metadata attached */ - def analyse(implicit context: ModuleContext) = { + def analyse(implicit context: ModuleContext): IR.Module = { BindingAnalysis.runModule(ir, context) } } @@ -82,7 +82,7 @@ class BindingAnalysisTest extends CompilerTest { ModuleMethod("enso_project"), ModuleMethod("foo") ) - metadata.currentModule shouldEqual ctx.module + metadata.currentModule shouldEqual ModuleReference.Concrete(ctx.module) } "properly assign module-level methods when a type with the same name as module is defined" in { diff --git a/engine/runtime/src/test/scala/org/enso/compiler/test/pass/resolve/MethodDefinitionsTest.scala b/engine/runtime/src/test/scala/org/enso/compiler/test/pass/resolve/MethodDefinitionsTest.scala index c39d82cf69..df362d5ca3 100644 --- a/engine/runtime/src/test/scala/org/enso/compiler/test/pass/resolve/MethodDefinitionsTest.scala +++ b/engine/runtime/src/test/scala/org/enso/compiler/test/pass/resolve/MethodDefinitionsTest.scala @@ -4,7 +4,7 @@ import org.enso.compiler.Passes import org.enso.compiler.context.{FreshNameSupply, ModuleContext} import org.enso.compiler.core.IR import org.enso.compiler.data.BindingsMap -import org.enso.compiler.data.BindingsMap.Cons +import org.enso.compiler.data.BindingsMap.{Cons, ModuleReference} import org.enso.compiler.pass.resolve.MethodDefinitions import org.enso.compiler.pass.{PassConfiguration, PassGroup, PassManager} import org.enso.compiler.test.CompilerTest @@ -77,7 +77,7 @@ class MethodDefinitionsTest extends CompilerTest { .getMetadata(MethodDefinitions) shouldEqual Some( BindingsMap.Resolution( BindingsMap.ResolvedConstructor( - ctx.module, + ModuleReference.Concrete(ctx.module), Cons("Foo", 3) ) ) @@ -88,7 +88,7 @@ class MethodDefinitionsTest extends CompilerTest { .typePointer .getMetadata(MethodDefinitions) shouldEqual Some( BindingsMap.Resolution( - BindingsMap.ResolvedModule(ctx.module) + BindingsMap.ResolvedModule(ModuleReference.Concrete(ctx.module)) ) ) ir.bindings(4) @@ -97,7 +97,7 @@ class MethodDefinitionsTest extends CompilerTest { .typePointer .getMetadata(MethodDefinitions) shouldEqual Some( BindingsMap.Resolution( - BindingsMap.ResolvedModule(ctx.module) + BindingsMap.ResolvedModule(ModuleReference.Concrete(ctx.module)) ) ) ir.bindings(5) @@ -112,12 +112,18 @@ class MethodDefinitionsTest extends CompilerTest { MethodDefinitions ) shouldEqual Some( BindingsMap.Resolution( - BindingsMap.ResolvedConstructor(ctx.module, Cons("Foo", 3)) + BindingsMap.ResolvedConstructor( + ModuleReference.Concrete(ctx.module), + Cons("Foo", 3) + ) ) ) conv1.sourceTypeName.getMetadata(MethodDefinitions) shouldEqual Some( BindingsMap.Resolution( - BindingsMap.ResolvedConstructor(ctx.module, Cons("Bar", 0)) + BindingsMap.ResolvedConstructor( + ModuleReference.Concrete(ctx.module), + Cons("Bar", 0) + ) ) ) @@ -128,7 +134,10 @@ class MethodDefinitionsTest extends CompilerTest { MethodDefinitions ) shouldEqual Some( BindingsMap.Resolution( - BindingsMap.ResolvedConstructor(ctx.module, Cons("Bar", 0)) + BindingsMap.ResolvedConstructor( + ModuleReference.Concrete(ctx.module), + Cons("Bar", 0) + ) ) ) conv2.sourceTypeName shouldBe an[IR.Error.Resolution] @@ -139,7 +148,10 @@ class MethodDefinitionsTest extends CompilerTest { conv3.methodReference.typePointer shouldBe an[IR.Error.Resolution] conv3.sourceTypeName.getMetadata(MethodDefinitions) shouldEqual Some( BindingsMap.Resolution( - BindingsMap.ResolvedConstructor(ctx.module, Cons("Foo", 3)) + BindingsMap.ResolvedConstructor( + ModuleReference.Concrete(ctx.module), + Cons("Foo", 3) + ) ) ) } diff --git a/engine/runtime/src/test/scala/org/enso/compiler/test/pass/resolve/PatternsTest.scala b/engine/runtime/src/test/scala/org/enso/compiler/test/pass/resolve/PatternsTest.scala index 423390ef51..7955327a00 100644 --- a/engine/runtime/src/test/scala/org/enso/compiler/test/pass/resolve/PatternsTest.scala +++ b/engine/runtime/src/test/scala/org/enso/compiler/test/pass/resolve/PatternsTest.scala @@ -5,6 +5,7 @@ import org.enso.compiler.context.{FreshNameSupply, ModuleContext} import org.enso.compiler.core.IR import org.enso.compiler.data.BindingsMap.{ Cons, + ModuleReference, Resolution, ResolvedConstructor } @@ -85,7 +86,12 @@ class PatternsTest extends CompilerTest { .asInstanceOf[IR.Pattern.Constructor] .constructor .getMetadata(Patterns) shouldEqual Some( - Resolution(ResolvedConstructor(ctx.module, Cons("Foo", 3))) + Resolution( + ResolvedConstructor( + ModuleReference.Concrete(ctx.module), + Cons("Foo", 3) + ) + ) ) patterns(1) shouldBe a[IR.Error.Pattern] patterns(2) diff --git a/engine/runtime/src/test/scala/org/enso/compiler/test/pass/resolve/UppercaseNamesTest.scala b/engine/runtime/src/test/scala/org/enso/compiler/test/pass/resolve/UppercaseNamesTest.scala index ad125c2b44..20c8495e07 100644 --- a/engine/runtime/src/test/scala/org/enso/compiler/test/pass/resolve/UppercaseNamesTest.scala +++ b/engine/runtime/src/test/scala/org/enso/compiler/test/pass/resolve/UppercaseNamesTest.scala @@ -5,6 +5,7 @@ import org.enso.compiler.context.{FreshNameSupply, ModuleContext} import org.enso.compiler.core.IR import org.enso.compiler.data.BindingsMap.{ Cons, + ModuleReference, Resolution, ResolvedConstructor, ResolvedModule @@ -94,7 +95,12 @@ class UppercaseNamesTest extends CompilerTest { .asInstanceOf[IR.Application.Prefix] .function .getMetadata(UppercaseNames) shouldEqual Some( - Resolution(ResolvedConstructor(ctx.module, Cons("My_Cons", 3))) + Resolution( + ResolvedConstructor( + ModuleReference.Concrete(ctx.module), + Cons("My_Cons", 3) + ) + ) ) } @@ -105,7 +111,7 @@ class UppercaseNamesTest extends CompilerTest { app.function.asInstanceOf[IR.Name.Literal].name shouldEqual "constant" app.arguments.length shouldEqual 1 app.arguments(0).value.getMetadata(UppercaseNames) shouldEqual Some( - Resolution(ResolvedModule(ctx.module)) + Resolution(ResolvedModule(ModuleReference.Concrete(ctx.module))) ) } @@ -116,7 +122,7 @@ class UppercaseNamesTest extends CompilerTest { app.function.asInstanceOf[IR.Name.Literal].name shouldEqual "add_one" app.arguments.length shouldEqual 2 app.arguments(0).value.getMetadata(UppercaseNames) shouldEqual Some( - Resolution(ResolvedModule(ctx.module)) + Resolution(ResolvedModule(ModuleReference.Concrete(ctx.module))) ) } @@ -124,7 +130,12 @@ class UppercaseNamesTest extends CompilerTest { val app = bodyExprs(3).asInstanceOf[IR.Application.Prefix] app.arguments.length shouldBe 3 app.function.getMetadata(UppercaseNames) shouldEqual Some( - Resolution(ResolvedConstructor(ctx.module, Cons("My_Cons", 3))) + Resolution( + ResolvedConstructor( + ModuleReference.Concrete(ctx.module), + Cons("My_Cons", 3) + ) + ) ) } diff --git a/engine/runtime/src/test/scala/org/enso/interpreter/test/instrument/RuntimeStdlibTest.scala b/engine/runtime/src/test/scala/org/enso/interpreter/test/instrument/RuntimeStdlibTest.scala index 6fb3cc98e6..04383ac48e 100644 --- a/engine/runtime/src/test/scala/org/enso/interpreter/test/instrument/RuntimeStdlibTest.scala +++ b/engine/runtime/src/test/scala/org/enso/interpreter/test/instrument/RuntimeStdlibTest.scala @@ -1,5 +1,6 @@ package org.enso.interpreter.test.instrument +import org.enso.compiler.pass.resolve.VectorLiterals import org.enso.distribution.FileSystem import org.enso.distribution.locking.ThreadSafeFileLockManager import org.enso.interpreter.test.Metadata @@ -204,7 +205,7 @@ class RuntimeStdlibTest ) ) if module.contains("Vector") => (xs.nonEmpty || as.nonEmpty) shouldBe true - xs.toVector.head.suggestion.module shouldEqual "Standard.Base.Data.Vector" + xs.toVector.head.suggestion.module shouldEqual VectorLiterals.vectorModuleName } stdlibSuggestions.nonEmpty shouldBe true