mirror of
https://github.com/enso-org/enso.git
synced 2024-11-22 20:56:39 +03:00
Resolve fully qualified names (#4056)
Added a separate pass, `FullyQualifiedNames`, that partially resolves fully qualified names. The pass only resolves the library part of the name and replaces it with a reference to the `Main` module. There are 2 scenarios that could be potentially: 1) the code uses a fully qualified name to a component that has been parsed/compiled 2) the code uses a fully qualified name to a component that has **not** be imported For the former case, it is sufficient to just check `PackageRepository` for the presence of the library name. In the latter we have to ensure that the library has been already parsed and all its imports are resolved. That would require the reference to `Compiler` in the `FullyQualifiedNames` pass, which could then trigger a full compilation for missing library. Since it has some undesired consequences (tracking of dependencies becomes rather complex) we decided to exclude that scenario until it is really needed. # Important Notes With this change, one can use a fully qualified name directly. e.g. ``` import Standard.Base main = Standard.Base.IO.println "Hello world!" ```
This commit is contained in:
parent
ed859c2682
commit
d463a43633
@ -517,6 +517,7 @@
|
||||
- [Introducing Meta.atom_with_hole][4023]
|
||||
- [Report failures in name resolution in type signatures][4030]
|
||||
- [Attach visualizations to sub-expressions][4048]
|
||||
- [Resolve Fully Qualified Names][4056]
|
||||
|
||||
[3227]: https://github.com/enso-org/enso/pull/3227
|
||||
[3248]: https://github.com/enso-org/enso/pull/3248
|
||||
@ -602,6 +603,7 @@
|
||||
[4023]: https://github.com/enso-org/enso/pull/4023
|
||||
[4030]: https://github.com/enso-org/enso/pull/4030
|
||||
[4048]: https://github.com/enso-org/enso/pull/4048
|
||||
[4056]: https://github.com/enso-org/enso/pull/4056
|
||||
|
||||
# Enso 2.0.0-alpha.18 (2021-10-12)
|
||||
|
||||
|
@ -242,11 +242,12 @@ public final class EnsoLanguage extends TruffleLanguage<EnsoContext> {
|
||||
);
|
||||
var inlineContext = new InlineContext(
|
||||
module,
|
||||
redirectConfigWithStrictErrors,
|
||||
scala.Some.apply(localScope),
|
||||
scala.Some.apply(false),
|
||||
scala.Option.empty(),
|
||||
scala.Option.empty(),
|
||||
redirectConfigWithStrictErrors
|
||||
scala.Option.empty()
|
||||
);
|
||||
Compiler silentCompiler = context.getCompiler().duplicateWithConfig(redirectConfigWithStrictErrors);
|
||||
scala.Option<ExpressionNode> exprNode;
|
||||
|
@ -322,7 +322,8 @@ class Compiler(
|
||||
val moduleContext = ModuleContext(
|
||||
module = module,
|
||||
freshNameSupply = Some(freshNameSupply),
|
||||
compilerConfig = config
|
||||
compilerConfig = config,
|
||||
pkgRepo = Some(packageRepository)
|
||||
)
|
||||
val compilerOutput = runMethodBodyPasses(module.getIr, moduleContext)
|
||||
module.unsafeSetIr(compilerOutput)
|
||||
|
@ -53,6 +53,9 @@ trait PackageRepository {
|
||||
libraryName: LibraryName
|
||||
): Either[PackageRepository.Error, Unit]
|
||||
|
||||
/** Checks if the library has already been loaded */
|
||||
def isPackageLoaded(libraryName: LibraryName): Boolean
|
||||
|
||||
/** Get a sequence of currently loaded packages. */
|
||||
def getLoadedPackages: Seq[Package[TruffleFile]]
|
||||
|
||||
@ -107,6 +110,9 @@ trait PackageRepository {
|
||||
|
||||
/** Modifies package and module names to reflect the project name change. */
|
||||
def renameProject(namespace: String, oldName: String, newName: String): Unit
|
||||
|
||||
/** Checks if any library with a given namespace has been registered */
|
||||
def isNamespaceRegistered(namespace: String): Boolean
|
||||
}
|
||||
|
||||
object PackageRepository {
|
||||
@ -526,6 +532,11 @@ object PackageRepository {
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
def isPackageLoaded(libraryName: LibraryName): Boolean = {
|
||||
loadedPackages.keySet.contains(libraryName)
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
override def getLoadedModules: Seq[Module] =
|
||||
loadedModules.values.toSeq
|
||||
@ -627,6 +638,9 @@ object PackageRepository {
|
||||
loadedModules.put(module.getName.toString, module)
|
||||
}
|
||||
}
|
||||
|
||||
override def isNamespaceRegistered(namespace: String): Boolean =
|
||||
loadedPackages.keySet.exists(_.namespace == namespace)
|
||||
}
|
||||
|
||||
/** Creates a [[PackageRepository]] for the run.
|
||||
|
@ -60,6 +60,7 @@ class Passes(
|
||||
List(
|
||||
ExpressionAnnotations,
|
||||
AliasAnalysis,
|
||||
FullyQualifiedNames,
|
||||
GlobalNames,
|
||||
TypeNames,
|
||||
MethodCalls,
|
||||
|
@ -1,5 +1,6 @@
|
||||
package org.enso.compiler.context
|
||||
|
||||
import org.enso.compiler.PackageRepository
|
||||
import org.enso.compiler.data.CompilerConfig
|
||||
import org.enso.compiler.pass.PassConfiguration
|
||||
import org.enso.interpreter.node.BaseNode.TailStatus
|
||||
@ -10,20 +11,22 @@ import org.enso.interpreter.runtime.scope.{LocalScope, ModuleScope}
|
||||
* expression.
|
||||
*
|
||||
* @param module the module in which the expression is being executed
|
||||
* @param compilerConfig the compiler configuration
|
||||
* @param localScope the local scope in which the expression is being executed
|
||||
* @param isInTailPosition whether or not the inline expression occurs in tail
|
||||
* position ([[None]] indicates no information)
|
||||
* @param freshNameSupply the compiler's supply of fresh names
|
||||
* @param passConfiguration the pass configuration
|
||||
* @param compilerConfig the compiler configuration
|
||||
* @param pkgRepo the compiler's package repository
|
||||
*/
|
||||
case class InlineContext(
|
||||
module: Module,
|
||||
compilerConfig: CompilerConfig,
|
||||
localScope: Option[LocalScope] = None,
|
||||
isInTailPosition: Option[Boolean] = None,
|
||||
freshNameSupply: Option[FreshNameSupply] = None,
|
||||
passConfiguration: Option[PassConfiguration] = None,
|
||||
compilerConfig: CompilerConfig
|
||||
pkgRepo: Option[PackageRepository] = None
|
||||
)
|
||||
object InlineContext {
|
||||
|
||||
@ -63,7 +66,8 @@ object InlineContext {
|
||||
isInTailPosition = None,
|
||||
freshNameSupply = moduleContext.freshNameSupply,
|
||||
passConfiguration = moduleContext.passConfiguration,
|
||||
compilerConfig = moduleContext.compilerConfig
|
||||
compilerConfig = moduleContext.compilerConfig,
|
||||
pkgRepo = moduleContext.pkgRepo
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package org.enso.compiler.context
|
||||
|
||||
import org.enso.compiler.PackageRepository
|
||||
import org.enso.compiler.data.CompilerConfig
|
||||
import org.enso.compiler.pass.PassConfiguration
|
||||
import org.enso.interpreter.runtime.Module
|
||||
@ -10,11 +11,14 @@ import org.enso.interpreter.runtime.Module
|
||||
* @param freshNameSupply the compiler's supply of fresh names
|
||||
* @param passConfiguration the pass configuration
|
||||
* @param compilerConfig the compiler configuration
|
||||
* @param isGeneratingDocs if true, should generate docs for IR
|
||||
* @param pkgRepo the compiler's package repository
|
||||
*/
|
||||
case class ModuleContext(
|
||||
module: Module,
|
||||
compilerConfig: CompilerConfig,
|
||||
freshNameSupply: Option[FreshNameSupply] = None,
|
||||
passConfiguration: Option[PassConfiguration] = None,
|
||||
isGeneratingDocs: Boolean = false,
|
||||
compilerConfig: CompilerConfig
|
||||
pkgRepo: Option[PackageRepository] = None
|
||||
)
|
||||
|
@ -7395,6 +7395,13 @@ object IR {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
case class MissingLibraryImportInFQNError(namespace: String)
|
||||
extends Reason {
|
||||
override def explain(originalName: IR.Name): String =
|
||||
s"Fully qualified name references a library $namespace.${originalName.name} but an import statement for it is missing."
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** A representation of an error resulting from wrong pattern matches.
|
||||
|
@ -22,7 +22,7 @@ case object Imports extends IRPass {
|
||||
/** The passes that are invalidated by running this pass. */
|
||||
override val invalidatedPasses: Seq[IRPass] = Seq()
|
||||
|
||||
private val mainModuleName =
|
||||
val mainModuleName =
|
||||
IR.Name.Literal(
|
||||
"Main",
|
||||
isMethod = false,
|
||||
|
@ -0,0 +1,367 @@
|
||||
package org.enso.compiler.pass.resolve
|
||||
|
||||
import org.enso.compiler.{Compiler, PackageRepository}
|
||||
import org.enso.compiler.context.{FreshNameSupply, InlineContext, ModuleContext}
|
||||
import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.core.IR.Error.Resolution.MissingLibraryImportInFQNError
|
||||
import org.enso.compiler.core.ir.MetadataStorage.ToPair
|
||||
import org.enso.compiler.data.BindingsMap
|
||||
import org.enso.compiler.data.BindingsMap.{ModuleReference, Resolution}
|
||||
import org.enso.compiler.exception.CompilerError
|
||||
import org.enso.compiler.pass.IRPass
|
||||
import org.enso.compiler.pass.analyse.{AliasAnalysis, BindingAnalysis}
|
||||
import org.enso.compiler.pass.desugar.Imports
|
||||
import org.enso.editions.LibraryName
|
||||
|
||||
/** Partially resolves fully qualified names corresponding to the library names
|
||||
*
|
||||
* 1. Identifies potential library names e.g., `Standard.Base`
|
||||
* 2. If the component has not be compiled yet, compilation is triggered
|
||||
* 3. Replaces the library name with a fresh name and a resolved Main module
|
||||
*/
|
||||
case object FullyQualifiedNames extends IRPass {
|
||||
|
||||
/** The type of the metadata object that the pass writes to the IR. */
|
||||
override type Metadata = FullyQualifiedNames.FQNResolution
|
||||
|
||||
/** The type of configuration for the pass. */
|
||||
override type Config = IRPass.Configuration.Default
|
||||
|
||||
/** The passes that this pass depends _directly_ on to run. */
|
||||
override val precursorPasses: Seq[IRPass] =
|
||||
Seq(AliasAnalysis, BindingAnalysis)
|
||||
|
||||
/** The passes that are invalidated by running this pass. */
|
||||
override val invalidatedPasses: Seq[IRPass] = Nil
|
||||
|
||||
/** Executes the pass on the provided `ir`, and returns a possibly transformed
|
||||
* or annotated version of `ir`.
|
||||
*
|
||||
* @param ir the Enso IR to process
|
||||
* @param moduleContext a context object that contains the information needed
|
||||
* to process a module
|
||||
* @return `ir`, possibly having made transformations or annotations to that
|
||||
* IR.
|
||||
*/
|
||||
override def runModule(
|
||||
ir: IR.Module,
|
||||
moduleContext: ModuleContext
|
||||
): IR.Module = {
|
||||
val scopeMap = ir.unsafeGetMetadata(
|
||||
BindingAnalysis,
|
||||
"No binding analysis on the module"
|
||||
)
|
||||
val freshNameSupply = moduleContext.freshNameSupply.getOrElse(
|
||||
throw new CompilerError(
|
||||
"No fresh name supply passed to UppercaseNames resolver."
|
||||
)
|
||||
)
|
||||
val new_bindings =
|
||||
ir.bindings.map(
|
||||
processModuleDefinition(
|
||||
_,
|
||||
scopeMap,
|
||||
freshNameSupply,
|
||||
moduleContext.pkgRepo
|
||||
)
|
||||
)
|
||||
ir.copy(bindings = new_bindings)
|
||||
|
||||
}
|
||||
|
||||
/** Executes the pass on the provided `ir`, and returns a possibly transformed
|
||||
* or annotated version of `ir` in an inline context.
|
||||
*
|
||||
* @param ir the Enso IR to process
|
||||
* @param inlineContext a context object that contains the information needed
|
||||
* for inline evaluation
|
||||
* @return `ir`, possibly having made transformations or annotations to that
|
||||
* IR.
|
||||
*/
|
||||
override def runExpression(
|
||||
ir: IR.Expression,
|
||||
inlineContext: InlineContext
|
||||
): IR.Expression = {
|
||||
val scopeMap = inlineContext.module.getIr.unsafeGetMetadata(
|
||||
BindingAnalysis,
|
||||
"No binding analysis on the module"
|
||||
)
|
||||
val freshNameSupply = inlineContext.freshNameSupply.getOrElse(
|
||||
throw new CompilerError(
|
||||
"No fresh name supply passed to UppercaseNames resolver."
|
||||
)
|
||||
)
|
||||
processExpression(
|
||||
ir,
|
||||
scopeMap,
|
||||
freshNameSupply,
|
||||
None,
|
||||
inlineContext.pkgRepo
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
private def processModuleDefinition(
|
||||
definition: IR.Module.Scope.Definition,
|
||||
bindings: BindingsMap,
|
||||
freshNameSupply: FreshNameSupply,
|
||||
pkgRepo: Option[PackageRepository]
|
||||
): IR.Module.Scope.Definition = {
|
||||
definition match {
|
||||
case asc: IR.Type.Ascription => asc
|
||||
case method: IR.Module.Scope.Definition.Method =>
|
||||
val resolution = method.methodReference.typePointer.flatMap(
|
||||
_.getMetadata(MethodDefinitions)
|
||||
)
|
||||
method.mapExpressions(
|
||||
processExpression(_, bindings, freshNameSupply, resolution, pkgRepo)
|
||||
)
|
||||
case tp: IR.Module.Scope.Definition.Type =>
|
||||
tp.copy(members =
|
||||
tp.members.map(
|
||||
_.mapExpressions(
|
||||
processExpression(
|
||||
_,
|
||||
bindings,
|
||||
freshNameSupply,
|
||||
bindings.resolveName(tp.name.name).toOption.map(Resolution),
|
||||
pkgRepo
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
case a =>
|
||||
a.mapExpressions(
|
||||
processExpression(_, bindings, freshNameSupply, None, pkgRepo)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private def processExpression(
|
||||
ir: IR.Expression,
|
||||
bindings: BindingsMap,
|
||||
freshNameSupply: FreshNameSupply,
|
||||
selfTypeResolution: Option[Resolution],
|
||||
pkgRepo: Option[PackageRepository]
|
||||
): IR.Expression =
|
||||
ir.transformExpressions {
|
||||
case lit: IR.Name.Literal =>
|
||||
if (!lit.isMethod && !isLocalVar(lit)) {
|
||||
val resolution = bindings.resolveName(lit.name)
|
||||
resolution match {
|
||||
case Left(_) =>
|
||||
if (
|
||||
pkgRepo
|
||||
.map(_.isNamespaceRegistered(lit.name))
|
||||
.getOrElse(false)
|
||||
) {
|
||||
lit.updateMetadata(
|
||||
this -->> FQNResolution(ResolvedLibrary(lit.name))
|
||||
)
|
||||
} else {
|
||||
lit
|
||||
}
|
||||
case Right(_) =>
|
||||
lit
|
||||
}
|
||||
} else {
|
||||
lit
|
||||
}
|
||||
case app @ IR.Application.Prefix(_, List(_), _, _, _, _) =>
|
||||
app.function match {
|
||||
case lit: IR.Name.Literal =>
|
||||
if (lit.isMethod)
|
||||
resolveLocalApplication(
|
||||
app,
|
||||
bindings,
|
||||
freshNameSupply,
|
||||
pkgRepo,
|
||||
selfTypeResolution
|
||||
)
|
||||
else
|
||||
app.mapExpressions(
|
||||
processExpression(
|
||||
_,
|
||||
bindings,
|
||||
freshNameSupply,
|
||||
selfTypeResolution,
|
||||
pkgRepo
|
||||
)
|
||||
)
|
||||
case _ =>
|
||||
app.mapExpressions(
|
||||
processExpression(
|
||||
_,
|
||||
bindings,
|
||||
freshNameSupply,
|
||||
selfTypeResolution,
|
||||
pkgRepo
|
||||
)
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private def resolveLocalApplication(
|
||||
app: IR.Application.Prefix,
|
||||
bindings: BindingsMap,
|
||||
freshNameSupply: FreshNameSupply,
|
||||
pkgRepo: Option[PackageRepository],
|
||||
selfTypeResolution: Option[Resolution]
|
||||
): IR.Expression = {
|
||||
val processedFun =
|
||||
processExpression(
|
||||
app.function,
|
||||
bindings,
|
||||
freshNameSupply,
|
||||
selfTypeResolution,
|
||||
pkgRepo
|
||||
)
|
||||
val processedArgs =
|
||||
app.arguments.map(
|
||||
_.mapExpressions(
|
||||
processExpression(
|
||||
_,
|
||||
bindings,
|
||||
freshNameSupply,
|
||||
selfTypeResolution,
|
||||
pkgRepo
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val processedApp = processedArgs match {
|
||||
case List(thisArg) =>
|
||||
(thisArg.value.getMetadata(this).map(_.target), processedFun) match {
|
||||
case (Some(resolved @ ResolvedLibrary(_)), name: IR.Name.Literal) =>
|
||||
resolveQualName(resolved, name, pkgRepo).fold(
|
||||
err => Some(err),
|
||||
_.map(resolvedMod =>
|
||||
freshNameSupply
|
||||
.newName()
|
||||
.updateMetadata(this -->> resolvedMod)
|
||||
.setLocation(name.location)
|
||||
)
|
||||
)
|
||||
case _ =>
|
||||
None
|
||||
}
|
||||
case _ =>
|
||||
None
|
||||
}
|
||||
|
||||
processedApp.getOrElse(
|
||||
app.copy(function = processedFun, arguments = processedArgs)
|
||||
)
|
||||
}
|
||||
|
||||
private def resolveQualName(
|
||||
thisResolution: ResolvedLibrary,
|
||||
consName: IR.Name.Literal,
|
||||
optPkgRepo: Option[PackageRepository]
|
||||
): Either[IR.Expression, Option[FQNResolution]] = {
|
||||
optPkgRepo
|
||||
.flatMap { pkgRepo =>
|
||||
val libName = LibraryName(thisResolution.namespace, consName.name)
|
||||
if (pkgRepo.isPackageLoaded(libName)) {
|
||||
pkgRepo
|
||||
.getLoadedModule(
|
||||
s"${libName.toString}.${Imports.mainModuleName.name}"
|
||||
)
|
||||
.map { m =>
|
||||
if (m.getIr == null) {
|
||||
// Limitation of Fully Qualified Names:
|
||||
// If the library has not been imported explicitly, then we won't have
|
||||
// IR for it. Triggering a full compilation at this stage may have
|
||||
// undesired consequences and is therefore prohibited on purpose.
|
||||
Left(
|
||||
IR.Error.Resolution(
|
||||
consName,
|
||||
MissingLibraryImportInFQNError(thisResolution.namespace)
|
||||
)
|
||||
)
|
||||
} else {
|
||||
Right(
|
||||
Some(
|
||||
FQNResolution(ResolvedModule(ModuleReference.Concrete(m)))
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Some(
|
||||
Left(
|
||||
IR.Error.Resolution(
|
||||
consName,
|
||||
MissingLibraryImportInFQNError(thisResolution.namespace)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
.getOrElse(Right(None))
|
||||
}
|
||||
|
||||
/** Updates the metadata in a copy of the IR when updating that metadata
|
||||
* requires global state.
|
||||
*
|
||||
* This is usually the case in the presence of structures that are shared
|
||||
* throughout the IR, and need to maintain that sharing for correctness. This
|
||||
* must be called with `copyOfIr` as the result of an `ir.duplicate` call.
|
||||
*
|
||||
* Additionally this method _must not_ alter the structure of the IR. It
|
||||
* should only update its metadata.
|
||||
*
|
||||
* @param sourceIr the IR being copied
|
||||
* @param copyOfIr a duplicate of `sourceIr`
|
||||
* @tparam T the concrete [[IR]] type
|
||||
* @return the result of updating metadata in `copyOfIr` globally using
|
||||
* information from `sourceIr`
|
||||
*/
|
||||
override def updateMetadataInDuplicate[T <: IR](sourceIr: T, copyOfIr: T): T =
|
||||
copyOfIr
|
||||
|
||||
private def isLocalVar(name: IR.Name.Literal): Boolean = {
|
||||
val aliasInfo = name
|
||||
.unsafeGetMetadata(
|
||||
AliasAnalysis,
|
||||
"no alias analysis info on a name"
|
||||
)
|
||||
.unsafeAs[AliasAnalysis.Info.Occurrence]
|
||||
val defLink = aliasInfo.graph.defLinkFor(aliasInfo.id)
|
||||
defLink.isDefined
|
||||
}
|
||||
|
||||
/** The FQN resolution metadata for a node.
|
||||
*
|
||||
* @param target the partially resolved name
|
||||
*/
|
||||
sealed case class FQNResolution(target: PartiallyResolvedFQN)
|
||||
extends IRPass.Metadata {
|
||||
|
||||
override val metadataName: String =
|
||||
"FullyQualifiedNames.Resolution"
|
||||
|
||||
/** @inheritdoc */
|
||||
override def prepareForSerialization(compiler: Compiler): FQNResolution =
|
||||
this
|
||||
|
||||
/** @inheritdoc */
|
||||
override def restoreFromSerialization(
|
||||
compiler: Compiler
|
||||
): Option[FQNResolution] =
|
||||
Some(this)
|
||||
|
||||
/** @inheritdoc */
|
||||
override def duplicate(): Option[IRPass.Metadata] = Some(this)
|
||||
}
|
||||
|
||||
sealed trait PartiallyResolvedFQN
|
||||
|
||||
case class ResolvedLibrary(namespace: String) extends PartiallyResolvedFQN
|
||||
case class ResolvedModule(moduleRef: ModuleReference)
|
||||
extends PartiallyResolvedFQN
|
||||
|
||||
}
|
@ -7,7 +7,8 @@ import org.enso.compiler.data.BindingsMap
|
||||
import org.enso.compiler.data.BindingsMap.{
|
||||
Resolution,
|
||||
ResolutionNotFound,
|
||||
ResolvedMethod
|
||||
ResolvedMethod,
|
||||
ResolvedModule
|
||||
}
|
||||
import org.enso.compiler.exception.CompilerError
|
||||
import org.enso.compiler.pass.IRPass
|
||||
@ -36,7 +37,7 @@ case object GlobalNames extends IRPass {
|
||||
|
||||
/** The passes that this pass depends _directly_ on to run. */
|
||||
override val precursorPasses: Seq[IRPass] =
|
||||
Seq(AliasAnalysis, BindingAnalysis)
|
||||
Seq(AliasAnalysis, BindingAnalysis, FullyQualifiedNames)
|
||||
|
||||
/** The passes that are invalidated by running this pass. */
|
||||
override val invalidatedPasses: Seq[IRPass] = Seq(AliasAnalysis)
|
||||
@ -150,53 +151,62 @@ case object GlobalNames extends IRPass {
|
||||
)
|
||||
)
|
||||
case lit: IR.Name.Literal =>
|
||||
if (!lit.isMethod && !isLocalVar(lit)) {
|
||||
val resolution = bindings.resolveName(lit.name)
|
||||
resolution match {
|
||||
case Left(error) =>
|
||||
IR.Error.Resolution(
|
||||
lit,
|
||||
IR.Error.Resolution.ResolverError(error)
|
||||
)
|
||||
case Right(r @ BindingsMap.ResolvedMethod(mod, method)) =>
|
||||
if (isInsideApplication) {
|
||||
lit.updateMetadata(this -->> BindingsMap.Resolution(r))
|
||||
} else {
|
||||
val self = freshNameSupply
|
||||
.newName()
|
||||
.updateMetadata(
|
||||
this -->> BindingsMap.Resolution(
|
||||
BindingsMap.ResolvedModule(mod)
|
||||
)
|
||||
)
|
||||
// The synthetic applications gets the location so that instrumentation
|
||||
// identifies the node correctly
|
||||
val fun = lit.copy(
|
||||
name = method.name,
|
||||
location = None
|
||||
lit.getMetadata(FullyQualifiedNames) match {
|
||||
case Some(
|
||||
FullyQualifiedNames.FQNResolution(
|
||||
FullyQualifiedNames.ResolvedModule(modRef)
|
||||
)
|
||||
val app = IR.Application.Prefix(
|
||||
fun,
|
||||
List(IR.CallArgument.Specified(None, self, None)),
|
||||
hasDefaultsSuspended = false,
|
||||
lit.location
|
||||
)
|
||||
fun
|
||||
.getMetadata(ExpressionAnnotations)
|
||||
.foreach(annotationsMeta =>
|
||||
app.updateMetadata(
|
||||
ExpressionAnnotations -->> annotationsMeta
|
||||
)
|
||||
) =>
|
||||
lit.updateMetadata(this -->> Resolution(ResolvedModule(modRef)))
|
||||
case _ =>
|
||||
if (!lit.isMethod && !isLocalVar(lit)) {
|
||||
val resolution = bindings.resolveName(lit.name)
|
||||
resolution match {
|
||||
case Left(error) =>
|
||||
IR.Error.Resolution(
|
||||
lit,
|
||||
IR.Error.Resolution.ResolverError(error)
|
||||
)
|
||||
fun.passData.remove(ExpressionAnnotations)
|
||||
app
|
||||
case Right(r @ BindingsMap.ResolvedMethod(mod, method)) =>
|
||||
if (isInsideApplication) {
|
||||
lit.updateMetadata(this -->> BindingsMap.Resolution(r))
|
||||
} else {
|
||||
val self = freshNameSupply
|
||||
.newName()
|
||||
.updateMetadata(
|
||||
this -->> BindingsMap.Resolution(
|
||||
BindingsMap.ResolvedModule(mod)
|
||||
)
|
||||
)
|
||||
// The synthetic applications gets the location so that instrumentation
|
||||
// identifies the node correctly
|
||||
val fun = lit.copy(
|
||||
name = method.name,
|
||||
location = None
|
||||
)
|
||||
val app = IR.Application.Prefix(
|
||||
fun,
|
||||
List(IR.CallArgument.Specified(None, self, None)),
|
||||
hasDefaultsSuspended = false,
|
||||
lit.location
|
||||
)
|
||||
fun
|
||||
.getMetadata(ExpressionAnnotations)
|
||||
.foreach(annotationsMeta =>
|
||||
app.updateMetadata(
|
||||
ExpressionAnnotations -->> annotationsMeta
|
||||
)
|
||||
)
|
||||
fun.passData.remove(ExpressionAnnotations)
|
||||
app
|
||||
}
|
||||
case Right(value) =>
|
||||
lit.updateMetadata(this -->> BindingsMap.Resolution(value))
|
||||
}
|
||||
case Right(value) =>
|
||||
lit.updateMetadata(this -->> BindingsMap.Resolution(value))
|
||||
}
|
||||
|
||||
} else {
|
||||
lit
|
||||
} else {
|
||||
lit
|
||||
}
|
||||
}
|
||||
case app: IR.Application.Prefix =>
|
||||
app.function match {
|
||||
|
@ -8,8 +8,6 @@ import org.enso.interpreter.runtime.Module
|
||||
import org.enso.interpreter.runtime.Module.CompilationStage
|
||||
import org.enso.syntax.text.Parser
|
||||
|
||||
import scala.annotation.unused
|
||||
|
||||
/** A phase responsible for initializing the builtins' IR from the provided
|
||||
* source.
|
||||
*/
|
||||
@ -30,9 +28,9 @@ object BuiltinsIrBuilder {
|
||||
* @param passes the compiler's pass manager
|
||||
*/
|
||||
def build(
|
||||
@unused module: Module,
|
||||
@unused freshNameSupply: FreshNameSupply,
|
||||
@unused passes: Passes
|
||||
module: Module,
|
||||
freshNameSupply: FreshNameSupply,
|
||||
passes: Passes
|
||||
): Unit = {
|
||||
val passManager = passes.passManager
|
||||
val moduleContext = ModuleContext(
|
||||
|
@ -0,0 +1,6 @@
|
||||
name: Test_Fully_Qualified_Name_Failure
|
||||
license: APLv2
|
||||
enso-version: default
|
||||
version: "0.0.1"
|
||||
author: "Enso Team <contact@enso.org>"
|
||||
maintainer: "Enso Team <contact@enso.org>"
|
@ -0,0 +1,3 @@
|
||||
main =
|
||||
Standard.Base.IO.println "Hello world!"
|
||||
0
|
@ -0,0 +1,6 @@
|
||||
name: Test_Fully_Qualified_Name_Success
|
||||
license: APLv2
|
||||
enso-version: default
|
||||
version: "0.0.1"
|
||||
author: "Enso Team <contact@enso.org>"
|
||||
maintainer: "Enso Team <contact@enso.org>"
|
@ -0,0 +1,5 @@
|
||||
import Standard.Base
|
||||
|
||||
main =
|
||||
Standard.Base.IO.println "Hello world!"
|
||||
0
|
@ -158,4 +158,26 @@ class ImportsTest extends PackageTest {
|
||||
"Constructors" should "be exportable" in {
|
||||
evalTestProject("Test_Type_Exports").toString shouldEqual "(Some 10)"
|
||||
}
|
||||
|
||||
"Fully qualified names" should "not be resolved when lacking imports" in {
|
||||
the[InterpreterException] thrownBy evalTestProject(
|
||||
"Test_Fully_Qualified_Name_Failure"
|
||||
) should have message "Compilation aborted due to errors."
|
||||
|
||||
val outLines = consumeOut
|
||||
outLines should have length 3
|
||||
outLines(
|
||||
2
|
||||
) shouldEqual "Main.enso[2:14-2:17]: Fully qualified name references a library Standard.Base but an import statement for it is missing."
|
||||
}
|
||||
|
||||
"Fully qualified names" should "be resolved when library has already been loaded" in {
|
||||
evalTestProject(
|
||||
"Test_Fully_Qualified_Name_Success"
|
||||
).toString shouldEqual "0"
|
||||
val outLines = consumeOut
|
||||
outLines should have length 1
|
||||
outLines(0) shouldEqual "Hello world!"
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -50,3 +50,7 @@ spec =
|
||||
Test.specify "should be allowed to be called statically" pending="Needs changes to method dispatch logic" <|
|
||||
b = Bar.Value 1
|
||||
Bar.meh b 2 . should_equal 3
|
||||
Test.group "Fully Qualified Names" <|
|
||||
Test.specify "should be correctly resolved" <|
|
||||
a = Standard.Base.Data.Array.Array.new 10
|
||||
a.length . should_equal 10
|
||||
|
Loading…
Reference in New Issue
Block a user