From 3a42d0ce76782369544838da601043f2fd46b468 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Tue, 2 May 2023 18:40:58 +0200 Subject: [PATCH] Reimplement `enso_project` as a proper builtin (#6352) Remove the magical code generation of `enso_project` method from codegen phase and reimplement it as a proper builtin method. The old behavior of `enso_project` was special, and violated the language semantics (regarding the `self` argument): - It was implicitly declared in every module, so it could be called without a self argument. - It can be called with explicit module as self argument, e.g. `Base.enso_project`, or `Visualizations.enso_project`. Let's avoid implicit methods on modules and let's be explicit. Let's reimplement the `enso_project` as a builtin method. To comply with the language semantics, we will have to change the signature a bit: - `enso_project` is a static method in the `Standard.Base.Meta.Enso_Project` module. - It takes an optional `project` argument (instead of taking it as an explicit self argument). Having the `enso_project` defined as a (shadowed) builtin method, we will automatically have suggestions created for it. # Important Notes - Truffle nodes are no longer generated in codegen phase for the `enso_project` method. It is a standard builtin now. - The minimal import to use `enso_project` is now `from Standard.Base.Meta.Enso_Project import enso_project`. - Tested implicitly by `org.enso.compiler.ExecCompilerTest#testInvalidEnsoProjectRef`. --- CHANGELOG.md | 2 + app/gui/src/controller/searcher.rs | 63 +------ build.sbt | 1 + .../lib/Standard/Base/0.0.0-dev/src/Main.enso | 2 + .../Base/0.0.0-dev/src/Meta/Enso_Project.enso | 35 ++++ .../Visualization/0.0.0-dev/src/Id.enso | 2 +- .../builtin/meta/EnsoProjectNode.java | 156 ++++++++++++++++++ .../expression/constant/EnsoProjectNode.java | 52 ------ .../interpreter/runtime/builtin/Builtins.java | 6 +- .../enso/compiler/SerializationManager.scala | 2 +- .../enso/compiler/codegen/IrToTruffle.scala | 23 +-- .../org/enso/compiler/data/BindingsMap.scala | 16 +- .../pass/analyse/BindingAnalysis.scala | 6 +- .../org/enso/compiler/ExecCompilerTest.java | 6 +- .../pass/analyse/BindingAnalysisTest.scala | 2 - .../test/semantic/EnsoProjectTest.scala | 36 ++++ .../enso/interpreter/dsl/MethodProcessor.java | 4 +- .../dsl/model/MethodDefinition.java | 10 +- .../src/Semantic/Meta_Location_Spec.enso | 5 +- test/Visualization_Tests/src/Id_Spec.enso | 4 +- .../lib/Standard/Base/0.0.0-dev/src/Meta.enso | 9 +- 21 files changed, 277 insertions(+), 165 deletions(-) create mode 100644 engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/EnsoProjectNode.java delete mode 100644 engine/runtime/src/main/java/org/enso/interpreter/node/expression/constant/EnsoProjectNode.java create mode 100644 engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/EnsoProjectTest.scala diff --git a/CHANGELOG.md b/CHANGELOG.md index 850692d19d09..c14c4f022487 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -719,6 +719,7 @@ - [Removing need for asynchronous thread to execute ResourceManager finalizers][6335] - [Warning.get_all returns only unique warnings][6372] +- [Reimplement `enso_project` as a proper builtin][6352] [3227]: https://github.com/enso-org/enso/pull/3227 [3248]: https://github.com/enso-org/enso/pull/3248 @@ -827,6 +828,7 @@ [6171]: https://github.com/enso-org/enso/pull/6171 [6335]: https://github.com/enso-org/enso/pull/6335 [6372]: https://github.com/enso-org/enso/pull/6372 +[6352]: https://github.com/enso-org/enso/pull/6352 # Enso 2.0.0-alpha.18 (2021-10-12) diff --git a/app/gui/src/controller/searcher.rs b/app/gui/src/controller/searcher.rs index 54ca7285a3be..2d1f2cba95d9 100644 --- a/app/gui/src/controller/searcher.rs +++ b/app/gui/src/controller/searcher.rs @@ -10,7 +10,6 @@ use crate::model::module::NodeMetadata; use crate::model::suggestion_database; use breadcrumbs::Breadcrumbs; -use const_format::concatcp; use double_representation::graph::GraphInfo; use double_representation::graph::LocationHint; use double_representation::import; @@ -50,12 +49,6 @@ pub use action::Action; /// See: https://github.com/enso-org/ide/issues/1067 pub const ASSIGN_NAMES_FOR_NODES: bool = true; -/// The special module used for mock `Enso_Project.data` entry. -/// See also [`Searcher::add_enso_project_entries`]. -const ENSO_PROJECT_SPECIAL_MODULE: &str = - concatcp!(project::STANDARD_BASE_LIBRARY_PATH, ".Enso_Project"); - - // ============== // === Errors === @@ -862,13 +855,7 @@ impl Searcher { }) .flatten(); let mut module = self.module(); - // TODO[ao] this is a temporary workaround. See [`Searcher::add_enso_project_entries`] - // documentation. - let enso_project_special_import = suggestion_database::entry::Import::Qualified { - module: ENSO_PROJECT_SPECIAL_MODULE.try_into().unwrap(), - }; - let without_enso_project = imports.filter(|i| *i != enso_project_special_import); - for entry_import in without_enso_project { + for entry_import in imports { let already_imported = module.iter_imports().any(|existing| entry_import.covered_by(&existing)); let import: import::Info = entry_import.into(); @@ -1029,9 +1016,6 @@ impl Searcher { } let libraries_cat = libraries_root_cat.add_category("Libraries", libraries_icon.clone_ref()); - if should_add_additional_entries { - Self::add_enso_project_entries(&libraries_cat); - } let entries = completion_response.results.iter().filter_map(|id| { self.database .lookup(*id) @@ -1093,34 +1077,6 @@ impl Searcher { fn module_qualified_name(&self) -> QualifiedName { self.graph.module_qualified_name(&*self.project) } - - /// Add to the action list the special mocked entry of `Enso_Project.data`. - /// - /// This is a workaround for Engine bug https://github.com/enso-org/enso/issues/1605. - //TODO[ao] this is a temporary workaround. - fn add_enso_project_entries(libraries_cat_builder: &action::CategoryBuilder) { - // We may unwrap here, because the constant is tested to be convertible to - // [`QualifiedName`]. - let module = QualifiedName::from_text(ENSO_PROJECT_SPECIAL_MODULE).unwrap(); - let self_type = module.clone(); - for method in &["data", "root"] { - let entry = model::suggestion_database::Entry { - name: (*method).to_owned(), - kind: model::suggestion_database::entry::Kind::Method, - defined_in: module.clone(), - arguments: vec![], - return_type: "Standard.Base.System.File.File".try_into().unwrap(), - documentation: vec![], - self_type: Some(self_type.clone()), - is_static: true, - scope: model::suggestion_database::entry::Scope::Everywhere, - icon_name: None, - reexported_in: None, - }; - let action = Action::Suggestion(action::Suggestion::FromDatabase(Rc::new(entry))); - libraries_cat_builder.add_action(action); - } - } } @@ -1346,13 +1302,6 @@ pub mod test { use parser::Parser; use std::assert_matches::assert_matches; - - #[test] - fn enso_project_special_module_is_convertible_to_qualified_names() { - QualifiedName::from_text(ENSO_PROJECT_SPECIAL_MODULE) - .expect("ENSO_PROJECT_SPECIAL_MODULE should be convertible to QualifiedName."); - } - pub fn completion_response(results: &[SuggestionId]) -> language_server::response::Completion { language_server::response::Completion { results: results.to_vec(), @@ -1682,11 +1631,11 @@ pub mod test { assert!(searcher.actions().is_loading()); fixture.test.run_until_stalled(); let list = searcher.actions().list().unwrap().to_action_vec(); - // There are 8 entries, because: 2 were returned from `completion` method, two are mocked, - // and all of these are repeated in "All Search Result" category. - assert_eq!(list.len(), 8); - assert_eq!(list[2], Action::Suggestion(action::Suggestion::FromDatabase(test_function_1))); - assert_eq!(list[3], Action::Suggestion(action::Suggestion::FromDatabase(test_function_2))); + // There are 4 entries, because: 2 were returned from `completion` method, and two are + // mocked. + assert_eq!(list.len(), 4); + assert_eq!(list[0], Action::Suggestion(action::Suggestion::FromDatabase(test_function_1))); + assert_eq!(list[1], Action::Suggestion(action::Suggestion::FromDatabase(test_function_2))); let notification = subscriber.next().boxed_local().expect_ready(); assert_eq!(notification, Some(Notification::NewActionList)); } diff --git a/build.sbt b/build.sbt index 63bb5f2cd625..0f6e9db905de 100644 --- a/build.sbt +++ b/build.sbt @@ -1075,6 +1075,7 @@ lazy val `polyglot-api` = project lazy val `language-server` = (project in file("engine/language-server")) .settings( + commands += WithDebugCommand.withDebug, frgaalJavaCompilerSetting, libraryDependencies ++= akka ++ circe ++ Seq( "com.typesafe.scala-logging" %% "scala-logging" % scalaLoggingVersion, diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso index 6f978b0b6fc3..d8d4cfc58bb7 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso @@ -12,6 +12,7 @@ import project.Function import project.IO import project.Math import project.Meta +from project.Meta.Enso_Project import enso_project import project.Nothing.Nothing import project.Panic.Panic import project.Polyglot.Java @@ -42,6 +43,7 @@ export project.Errors export project.IO export project.Math export project.Meta +from project.Meta.Enso_Project export enso_project export project.Nothing.Nothing export project.Panic.Panic export project.Polyglot.Java diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Meta/Enso_Project.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Meta/Enso_Project.enso index de3cdb8c2e46..9a9e4db539af 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Meta/Enso_Project.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Meta/Enso_Project.enso @@ -1,6 +1,10 @@ +import project.Any.Any import project.Data.Text.Text +import project.Nothing.Nothing import project.System.File.File +from project.Errors.Common import Module_Not_In_Package_Error + ## Functionality for inspecting the current project. @Builtin_Type type Project_Description @@ -47,3 +51,34 @@ type Project_Description enso_project.namespace namespace : Text namespace self = self.prim_config.namespace + +## Returns the Enso project description for the given module. If no module is + given, returns the description of the project that the engine was executed + with, i.e., the project that contains the `main` method, or throws + `Module_Not_In_Package_Error` if there is no such project, e.g., when + executing a single file. + + Arguments: + - module: An optional module for which the Enso project description + will be fetched. If `Nothing`, the current project description will + be fetched. + + > Example + Get the project description for the project that contains the `main` + method. + + enso_project + + > Example + Get the project description for the `Standard.Visualizations` project. + + enso_project Standard.Visualizations + + > Example + Get the project description for the `Standard.Base` project from the + `Vector` module. + + enso_project Standard.Base.Data.Vector +enso_project : (Any | Nothing) -> Project_Description ! Module_Not_In_Package_Error +enso_project module=Nothing = + Project_Description.enso_project_builtin module diff --git a/distribution/lib/Standard/Visualization/0.0.0-dev/src/Id.enso b/distribution/lib/Standard/Visualization/0.0.0-dev/src/Id.enso index 77f75989cd36..b711d024bc7f 100644 --- a/distribution/lib/Standard/Visualization/0.0.0-dev/src/Id.enso +++ b/distribution/lib/Standard/Visualization/0.0.0-dev/src/Id.enso @@ -80,4 +80,4 @@ type Id example_id = Visualization.Id.from_module Base "My Visualization" from_module module visualization_name = - Id.Library module.enso_project visualization_name + Id.Library (enso_project module) visualization_name diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/EnsoProjectNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/EnsoProjectNode.java new file mode 100644 index 000000000000..74b1ba6b389a --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/EnsoProjectNode.java @@ -0,0 +1,156 @@ +package org.enso.interpreter.node.expression.builtin.meta; + +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.Truffle; +import com.oracle.truffle.api.TruffleFile; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.library.CachedLibrary; +import com.oracle.truffle.api.nodes.Node; +import java.lang.ref.WeakReference; +import java.util.Optional; +import org.enso.interpreter.dsl.BuiltinMethod; +import org.enso.interpreter.node.EnsoRootNode; +import org.enso.interpreter.runtime.EnsoContext; +import org.enso.interpreter.runtime.callable.atom.Atom; +import org.enso.interpreter.runtime.data.EnsoFile; +import org.enso.interpreter.runtime.data.Type; +import org.enso.interpreter.runtime.error.DataflowError; +import org.enso.interpreter.runtime.library.dispatch.TypesLibrary; +import org.enso.pkg.Package; + +@BuiltinMethod( + type = "Project_Description", + name = "enso_project_builtin", + description = "Returns the project description of the project given as the argument") +public abstract class EnsoProjectNode extends Node { + + public static EnsoProjectNode build() { + return EnsoProjectNodeGen.create(); + } + + /** + * It is OK to cache EnsoContext and project description atom here, since this node should be + * cloned into every separate caller's AST. + */ + @CompilationFinal + private WeakReference previousCtxRef = new WeakReference<>(null); + + @CompilationFinal + private WeakReference cachedProjectDescrRef = new WeakReference<>(null); + + /** + * @param module Either {@code Nothing}, or a module. + */ + public abstract Object execute(Object module); + + /** + * Fetches the second stack frame from Truffle runtime (note that the first stack frame is the + * call of {@code enso_project}) - the caller of {@code enso_project}, and finds in which package + * the caller is located. + * + * @param nothing Nothing, or interop null. + */ + @Specialization(guards = "isNothing(nothing)") + public Object getCurrentProjectDescr(Object nothing) { + var ctx = EnsoContext.get(this); + var previousCtx = previousCtxRef.get(); + var cachedProjectDescr = cachedProjectDescrRef.get(); + if (previousCtx == null || cachedProjectDescr == null || previousCtx != ctx) { + CompilerDirectives.transferToInterpreter(); + previousCtxRef = new WeakReference<>(ctx); + // Find the caller of `enso_project`, i.e., of this node, and find in which package + // it is located. The first frame is skipped, because it is always + // `Enso_Project.enso_project`, + // i.e., the first frame is always call of this specialization. + Optional> pkgOpt = + Truffle.getRuntime() + .iterateFrames( + frame -> { + var callNode = frame.getCallNode(); + assert callNode != null + : "Should skip the first frame, therefore, callNode should not be null"; + var callRootNode = callNode.getRootNode(); + assert callRootNode != null + : "Should be called only from Enso code, and thus, should always have a root node"; + if (callRootNode instanceof EnsoRootNode ensoRootNode) { + var pkg = ensoRootNode.getModuleScope().getModule().getPackage(); + // Don't return null, as that would signal to Truffle that we want to + // continue the iteration. + if (pkg != null) { + return Optional.of(pkg); + } else { + return Optional.empty(); + } + } else { + throw new IllegalStateException( + "Should not reach here: callRootNode = " + + callRootNode + + ". Probably not called from Enso?"); + } + }, + // The first frame is always Enso_Project.enso_project + 1); + if (pkgOpt.isPresent()) { + cachedProjectDescrRef = new WeakReference<>( + createProjectDescriptionAtom(ctx, pkgOpt.get())); + } else { + cachedProjectDescrRef = new WeakReference<>(notInModuleError(ctx)); + } + cachedProjectDescr = cachedProjectDescrRef.get(); + } + assert cachedProjectDescr != null; + return cachedProjectDescr; + } + + @Specialization(guards = "!isNothing(module)") + @TruffleBoundary + public Object getOtherProjectDescr( + Object module, + @CachedLibrary(limit = "5") TypesLibrary typesLib) { + var ctx = EnsoContext.get(this); + if (!typesLib.hasType(module)) { + return unsupportedArgsError(module); + } + Type moduleType = typesLib.getType(module); + // Currently, the module is represented as Type with no constructors. + if (!moduleType.getConstructors().isEmpty()) { + return unsupportedArgsError(module); + } + var pkg = moduleType.getDefinitionScope().getModule().getPackage(); + if (pkg != null) { + return createProjectDescriptionAtom(ctx, pkg); + } else { + return notInModuleError(ctx); + } + } + + private static Atom createProjectDescriptionAtom(EnsoContext ctx, Package pkg) { + EnsoFile rootPath = new EnsoFile(pkg.root().normalize()); + Object cfg = ctx.getEnvironment().asGuestValue(pkg.config()); + return ctx.getBuiltins() + .getProjectDescription() + .getUniqueConstructor() + .newInstance(rootPath, cfg); + } + + private DataflowError unsupportedArgsError(Object moduleActual) { + return DataflowError.withoutTrace( + EnsoContext.get(this) + .getBuiltins() + .error() + .makeUnsupportedArgumentsError( + new Object[]{moduleActual}, "The `module` argument does not refer to a module"), + this); + } + + private DataflowError notInModuleError(EnsoContext ctx) { + return DataflowError.withoutTrace( + ctx.getBuiltins().error().makeModuleNotInPackageError(), this); + } + + boolean isNothing(Object object) { + return EnsoContext.get(this).getBuiltins().nothing() == object; + } +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/constant/EnsoProjectNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/constant/EnsoProjectNode.java deleted file mode 100644 index 83ea7f7435a6..000000000000 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/constant/EnsoProjectNode.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.enso.interpreter.node.expression.constant; - -import com.oracle.truffle.api.CompilerDirectives; -import com.oracle.truffle.api.TruffleFile; -import com.oracle.truffle.api.TruffleLanguage; -import com.oracle.truffle.api.frame.VirtualFrame; -import com.oracle.truffle.api.nodes.NodeInfo; -import com.oracle.truffle.api.nodes.RootNode; -import org.enso.interpreter.runtime.EnsoContext; -import org.enso.interpreter.runtime.data.EnsoFile; -import org.enso.interpreter.runtime.error.DataflowError; -import org.enso.pkg.Package; - -import java.util.Optional; - -@NodeInfo(description = "Returns a language-level representation of the given Enso project.") -public class EnsoProjectNode extends RootNode { - private final Optional> pkgOpt; - - public EnsoProjectNode( - TruffleLanguage language, EnsoContext context, Optional> pkgOpt) { - super(language); - this.pkgOpt = pkgOpt; - } - - @Override - public Object execute(VirtualFrame frame) { - return createProjectDescription(); - } - - @CompilerDirectives.TruffleBoundary - private Object createProjectDescription() { - var context = EnsoContext.get(this); - if (pkgOpt.isPresent()) { - Package pkg = pkgOpt.get(); - EnsoFile rootPath = new EnsoFile(pkg.root().normalize()); - Object cfg = context.getEnvironment().asGuestValue(pkg.config()); - var result = - context - .getBuiltins() - .getProjectDescription() - .getUniqueConstructor() - .newInstance(rootPath, cfg); - return result; - } else { - var result = - DataflowError.withoutTrace( - context.getBuiltins().error().makeModuleNotInPackageError(), this); - return result; - } - } -} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Builtins.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Builtins.java index 594c499bb642..53621ff76848 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Builtins.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Builtins.java @@ -57,11 +57,11 @@ public class Builtins { private static final List> loadedBuiltinConstructors; - private static final Map loadedBbuiltinMethods; + private static final Map loadedBuiltinMethods; static { loadedBuiltinConstructors = readBuiltinTypes(); - loadedBbuiltinMethods = readBuiltinMethodsMethods(); + loadedBuiltinMethods = readBuiltinMethodsMethods(); } public static final String PACKAGE_NAME = "Builtins"; @@ -130,7 +130,7 @@ public Builtins(EnsoContext context) { .collect( Collectors.toMap( v -> v.getType().getName(), java.util.function.Function.identity())); - builtinMethodNodes = readBuiltinMethodsMetadata(loadedBbuiltinMethods, scope); + builtinMethodNodes = readBuiltinMethodsMetadata(loadedBuiltinMethods, scope); registerBuiltinMethods(scope, language); error = new Error(this, context); diff --git a/engine/runtime/src/main/scala/org/enso/compiler/SerializationManager.scala b/engine/runtime/src/main/scala/org/enso/compiler/SerializationManager.scala index 4dcd2a3f631d..493bb005f246 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/SerializationManager.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/SerializationManager.scala @@ -371,7 +371,7 @@ final class SerializationManager( logger.log( debugLogLevel, "Restored IR from cache for module [{0}] at stage [{1}].", - Array(module.getName, loadedCache.compilationStage()) + Array[Object](module.getName, loadedCache.compilationStage()) ) if (!relinkedIrChecks.contains(false)) { 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 9980a306c2c9..e0724a3f27e5 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 @@ -155,7 +155,6 @@ class IrToTruffle( * @param module the module for which code should be generated */ private def processModule(module: IR.Module): Unit = { - generateMethods() generateReExportBindings(module) val bindingsMap = module @@ -393,7 +392,7 @@ class IrToTruffle( val builtinNameElements = fullMethodName.text.split('.') if (builtinNameElements.length != 2) { throw new CompilerError( - s"Unknwon builtin method ${fullMethodName.text}" + s"Unknown builtin method ${fullMethodName.text}, probably should be '$fullMethodDefName?'" ) } val methodName = builtinNameElements(1) @@ -723,26 +722,6 @@ class IrToTruffle( expr } - private def generateMethods(): Unit = { - generateEnsoProjectMethod() - } - - private def generateEnsoProjectMethod(): Unit = { - val name = BindingsMap.Generated.ensoProjectMethodName - val pkg = context.getPackageOf(moduleScope.getModule.getSourceFile) - val ensoProjectNode = new EnsoProjectNode(language, context, pkg) - val body = ensoProjectNode.getCallTarget - val schema = new FunctionSchema( - new ArgumentDefinition( - 0, - Constants.Names.SELF_ARGUMENT, - ArgumentDefinition.ExecutionMode.EXECUTE - ) - ) - val fun = new RuntimeFunction(body, null, schema) - moduleScope.registerMethod(moduleScope.getAssociatedType, name, fun) - } - private def generateReExportBindings(module: IR.Module): Unit = { def mkConsGetter(constructor: AtomConstructor): RuntimeFunction = { new RuntimeFunction( 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 5ef93076ff8a..791c124e0c97 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 @@ -263,10 +263,8 @@ case class BindingsMap( def getDirectlyExportedModules: List[ExportedModule] = resolvedImports.collect { case ResolvedImport(_, exports, mod) => exports.map { exp => - val hidingEnsoProject = - SymbolRestriction.Hiding(Set(Generated.ensoProjectMethodName)) val restriction = if (exp.isAll) { - val definedRestriction = if (exp.onlyNames.isDefined) { + if (exp.onlyNames.isDefined) { SymbolRestriction.Only( exp.onlyNames.get .map(name => @@ -282,9 +280,6 @@ case class BindingsMap( } else { SymbolRestriction.All } - SymbolRestriction.Intersect( - List(hidingEnsoProject, definedRestriction) - ) } else { SymbolRestriction.Only( Set( @@ -317,15 +312,6 @@ object BindingsMap { } } - /** Utilities for methods automatically generated by the compiler. - */ - object Generated { - - /** The name of the builtin `enso_project` method. - */ - val ensoProjectMethodName: String = "enso_project" - } - /** Represents a symbol restriction on symbols exported from a module. */ sealed trait SymbolRestriction { 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 a20dc6de1cab..aeb85c7fd494 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 @@ -93,13 +93,9 @@ case object BindingAnalysis extends IRPass { } .flatten .map(BindingsMap.ModuleMethod) - val methodsWithAutogen = - BindingsMap.ModuleMethod( - BindingsMap.Generated.ensoProjectMethodName - ) :: moduleMethods ir.updateMetadata( this -->> BindingsMap( - definedSumTypes ++ importedPolyglot ++ methodsWithAutogen, + definedSumTypes ++ importedPolyglot ++ moduleMethods, ModuleReference.Concrete(moduleContext.module) ) ) diff --git a/engine/runtime/src/test/java/org/enso/compiler/ExecCompilerTest.java b/engine/runtime/src/test/java/org/enso/compiler/ExecCompilerTest.java index b4434c92cc91..08f424e35590 100644 --- a/engine/runtime/src/test/java/org/enso/compiler/ExecCompilerTest.java +++ b/engine/runtime/src/test/java/org/enso/compiler/ExecCompilerTest.java @@ -68,8 +68,12 @@ public void testHalfAssignment() throws Exception { @Test public void testInvalidEnsoProjectRef() throws Exception { - var module = ctx.eval("enso", """ + var module = + ctx.eval( + "enso", + """ from Standard.Base.Errors.Common import all + from Standard.Base.Meta.Enso_Project import enso_project run dummy = _ = dummy (enso_project.data / "foo").to_display_text 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 6d9394475781..6f57b8fce092 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 @@ -83,7 +83,6 @@ class BindingAnalysisTest extends CompilerTest { Type("Baz", List(), false), PolyglotSymbol("MyClass"), PolyglotSymbol("Renamed_Class"), - ModuleMethod("enso_project"), ModuleMethod("foo") ) metadata.currentModule shouldEqual ModuleReference.Concrete(ctx.module) @@ -106,7 +105,6 @@ class BindingAnalysisTest extends CompilerTest { .get .definedEntities .filter(_.isInstanceOf[BindingsMap.ModuleMethod]) shouldEqual List( - ModuleMethod("enso_project"), ModuleMethod("bar") ) diff --git a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/EnsoProjectTest.scala b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/EnsoProjectTest.scala new file mode 100644 index 000000000000..551030e86563 --- /dev/null +++ b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/EnsoProjectTest.scala @@ -0,0 +1,36 @@ +package org.enso.interpreter.test.semantic + +import org.enso.interpreter.test.{InterpreterContext, InterpreterTest} + +class EnsoProjectTest extends InterpreterTest { + + override def subject: String = "Enso_Project.enso_project" + + override def specify(implicit + interpreterContext: InterpreterContext + ): Unit = { + "enso_project should be in micro-distribution" in { + val code = + """ + |from Standard.Base.Meta import enso_project + |from Standard.Base.Nothing import Nothing + | + |main = (enso_project Nothing).name + |""".stripMargin + eval( + code + ).toString shouldEqual "(Error: Module_Not_In_Package_Error.Error)" + } + + "enso_project for Standard.Base" in { + val code = + """ + |import Standard.Base + |from Standard.Base.Meta import enso_project + | + |main = (enso_project Standard.Base).name + |""".stripMargin + eval(code) shouldEqual "Base" + } + } +} diff --git a/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/MethodProcessor.java b/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/MethodProcessor.java index cc1c8b6856c4..1a298dbcf0a6 100644 --- a/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/MethodProcessor.java +++ b/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/MethodProcessor.java @@ -55,7 +55,7 @@ public boolean handleProcess(Set annotations, RoundEnviro if (elt.getKind() == ElementKind.CLASS) { try { var needsFrame = BuiltinsProcessor.checkNeedsFrame(elt); - handleTypeELement((TypeElement) elt, roundEnv, needsFrame); + handleTypeElement((TypeElement) elt, roundEnv, needsFrame); } catch (IOException e) { processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, e.getMessage()); } @@ -72,7 +72,7 @@ public boolean handleProcess(Set annotations, RoundEnviro return true; } - private void handleTypeELement(TypeElement element, RoundEnvironment roundEnv, Boolean needsFrame) + private void handleTypeElement(TypeElement element, RoundEnvironment roundEnv, Boolean needsFrame) throws IOException { ExecutableElement executeMethod = element.getEnclosedElements().stream() diff --git a/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/model/MethodDefinition.java b/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/model/MethodDefinition.java index c9835cd4eca9..ec4f8bc88201 100644 --- a/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/model/MethodDefinition.java +++ b/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/model/MethodDefinition.java @@ -86,7 +86,15 @@ private String initConstructor(TypeElement element) { if (useBuild) { return originalClassName + ".build()"; } else { - return "new " + originalClassName + "()"; + boolean isClassAbstract = element.getModifiers().contains(Modifier.ABSTRACT); + if (isClassAbstract) { + throw new RuntimeException( + "Class " + + element.getSimpleName() + + " is abstract, and has no static `build()` method."); + } else { + return "new " + originalClassName + "()"; + } } } diff --git a/test/Tests/src/Semantic/Meta_Location_Spec.enso b/test/Tests/src/Semantic/Meta_Location_Spec.enso index e80f5aa6a7a3..40dfe9d69a63 100644 --- a/test/Tests/src/Semantic/Meta_Location_Spec.enso +++ b/test/Tests/src/Semantic/Meta_Location_Spec.enso @@ -25,6 +25,9 @@ spec = Test.group "Meta-Value Inspection" <| Test.specify "should allow access to package names" <| enso_project.name.should_equal 'Tests' - Base.enso_project.name.should_equal 'Base' + (enso_project Base).name.should_equal 'Base' + + Test.specify "should allow to fetch enso project description from a module" <| + (enso_project Standard.Base.Data.Vector).name.should_equal "Base" main = Test_Suite.run_main spec diff --git a/test/Visualization_Tests/src/Id_Spec.enso b/test/Visualization_Tests/src/Id_Spec.enso index 5c0f05eea0fa..2e6855ccfe8f 100644 --- a/test/Visualization_Tests/src/Id_Spec.enso +++ b/test/Visualization_Tests/src/Id_Spec.enso @@ -5,7 +5,7 @@ from Standard.Table import Table import Standard.Visualization -from Standard.Test import Test +from Standard.Test import Test, Test_Suite import Standard.Test.Extensions type My_Type @@ -59,3 +59,5 @@ spec = Test.group "Serializable Visualization Identifiers" <| c_3_3 = ['name', ["foo", "bar", "baz"]] t_3 = Table.new [c_3_1, c_3_2, c_3_3] t_3.default_visualization.should_equal Visualization.Id.table + +main = Test_Suite.run_main spec diff --git a/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Meta.enso b/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Meta.enso index 2837626e4213..835047681a5f 100644 --- a/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Meta.enso +++ b/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Meta.enso @@ -1,4 +1,3 @@ - type Unresolved_Symbol Value value @@ -20,3 +19,11 @@ meta value = is_unresolved_symbol value = @Builtin_Method "Meta.is_unresolved_symbol" get_unresolved_symbol_name symbol = @Builtin_Method "Meta.get_unresolved_symbol_name" get_unresolved_symbol_scope symbol = @Builtin_Method "Meta.get_unresolved_symbol_scope" + +@Builtin_Type +type Project_Description + Value prim_root_file prim_config + + name self = self.prim_config.name + +enso_project module = Project_Description.enso_project_builtin module