mirror of
https://github.com/enso-org/enso.git
synced 2024-09-11 13:15:52 +03:00
Document the runtime's builtin functionality (#1397)
This commit is contained in:
parent
d30a80eedc
commit
a7bd90ab8f
@ -370,7 +370,7 @@ val directoryWatcherVersion = "0.9.10"
|
||||
val flatbuffersVersion = "1.12.0"
|
||||
val guavaVersion = "29.0-jre"
|
||||
val jlineVersion = "3.15.0"
|
||||
val kindProjectorVersion = "0.11.0"
|
||||
val kindProjectorVersion = "0.11.2"
|
||||
val mockitoScalaVersion = "1.14.8"
|
||||
val newtypeVersion = "0.4.4"
|
||||
val pprintVersion = "0.5.9"
|
||||
|
@ -2,9 +2,9 @@ import Builtins
|
||||
from Base import all
|
||||
|
||||
## Returns the root directory of the project.
|
||||
Builtins.Enso_Project.root : File.File
|
||||
Builtins.Enso_Project.root = File.File this.prim_root_file
|
||||
Builtins.Project_Description.root : File.File
|
||||
Builtins.Project_Description.root = File.File this.prim_root_file
|
||||
|
||||
## Returns the root data directory of the project.
|
||||
Builtins.Enso_Project.data : File.File
|
||||
Builtins.Enso_Project.data = this.root / "data"
|
||||
Builtins.Project_Description.data : File.File
|
||||
Builtins.Project_Description.data = this.root / "data"
|
||||
|
@ -29,3 +29,5 @@ dependencies, and Enso projects for use by our users.
|
||||
working.
|
||||
- [**Local Repository:**](local-repository.md) Explanation of local repository
|
||||
structure that is used for bundling engine with project manager distributions.
|
||||
- [**Standard Libraries:**](standard-libraries.md) A brief explanation of the
|
||||
standard libraries for Enso.
|
||||
|
151
docs/distribution/standard-libraries.md
Normal file
151
docs/distribution/standard-libraries.md
Normal file
@ -0,0 +1,151 @@
|
||||
---
|
||||
layout: developer-doc
|
||||
title: Standard Libraries
|
||||
category: distribution
|
||||
tags: [distribution, stdlib]
|
||||
order: 9
|
||||
---
|
||||
|
||||
# Standard Libraries
|
||||
|
||||
At the current stage, Enso ships with a small set of libraries that compose the
|
||||
language's "standard library". This document provides a brief explanation of
|
||||
these libraries, as well as notes on how they should be used.
|
||||
|
||||
<!-- MarkdownTOC levels="2,3" autolink="true" indent=" " -->
|
||||
|
||||
- [Base](#base)
|
||||
- [Builtins](#builtins)
|
||||
- [Table](#table)
|
||||
- [Test](#test)
|
||||
- [Documentation](#documentation)
|
||||
- [General Libraries Notes](#general-libraries-notes)
|
||||
|
||||
<!-- /MarkdownTOC -->
|
||||
|
||||
## Base
|
||||
|
||||
`Base` is the core library of Enso. It contains core types and data structures,
|
||||
as well as basic functionality for interacting with the outside world. It can be
|
||||
found in
|
||||
[`distribution/std-lib/Base`](https://github.com/enso-org/enso/tree/main/distribution/std-lib/Base).
|
||||
|
||||
`Base` is intended to be imported unqualified at the top of the file:
|
||||
`from Base import all`. Items not included in this unqualified import are
|
||||
considered to be more specialist or internal, and should be intentionally
|
||||
imported by users.
|
||||
|
||||
### Builtins
|
||||
|
||||
In addition to the functionalities exposed in the standard library source, the
|
||||
interpreter also contains a set of definitions that are considered "primitive"
|
||||
and are hence built into the interpreter.
|
||||
|
||||
For the purposes of documentation, there is a
|
||||
[`Builtins.enso`](https://github.com/enso-org/enso/tree/main/engine/runtime/src/main/resources/Builtins.enso)
|
||||
file that provides stub definitions for these builtin functions. It is used for
|
||||
documentation purposes, and must be kept up to date as the builtins change.
|
||||
|
||||
> #### Note: Shadow Definitions
|
||||
>
|
||||
> In time this file will be replaced by true shadow definitions for the language
|
||||
> builtins. It is only a stop-gap measure to allow documenting this
|
||||
> functionality at this point in time.
|
||||
|
||||
## Table
|
||||
|
||||
`Table` is Enso's dataframes library, providing functionality for loading and
|
||||
analysing tabular data. It is a core data-science toolkit, that integrates
|
||||
deeply with Enso and its IDE. It can be found in
|
||||
[`distribution/std-lib/Table`](https://github.com/enso-org/enso/tree/main/distribution/std-lib/Table).
|
||||
|
||||
`Table` is designed to be imported unqualified: `from Table import all`. Items
|
||||
not included in this unqualified import are considered to be more specialist or
|
||||
internal, and should be intentionally imported by users.
|
||||
|
||||
## Test
|
||||
|
||||
`Test` is a library for testing and benchmarking Enso code. At this point in
|
||||
time it is _very_ rudimentary, and needs significant improvement before we can
|
||||
consider it an "official" part of the Enso standard libraries. It can be found
|
||||
in
|
||||
[`distribution/std-lib/Test`](https://github.com/enso-org/enso/tree/main/distribution/std-lib/Test).
|
||||
|
||||
`Test` is intended to be imported qualified: `import Test`. This ensures that
|
||||
there aren't spurious name clashes between user-defined functionality and the
|
||||
testing library.
|
||||
|
||||
## Documentation
|
||||
|
||||
These libraries are comprehensively documented, with all functionality
|
||||
accompanied by comprehensive documentation comments. These are located _above_
|
||||
each definition, for example:
|
||||
|
||||
```
|
||||
## Sort the Vector.
|
||||
|
||||
Arguments:
|
||||
- `on`: A projection from the element type to the value of that element
|
||||
being sorted on.
|
||||
- `by`: A function that compares the result of applying `on` to two
|
||||
elements, returning an Ordering to compare them.
|
||||
- `order`: The order in which the vector elements are sorted.
|
||||
|
||||
By default, elements are sorted in ascending order, using the comparator
|
||||
`compare_to`. A custom comparator may be passed to the sort function.
|
||||
|
||||
This is a stable sort, meaning that items that compare the same will not
|
||||
have their order changed by the sorting process.
|
||||
|
||||
The complexities for this sort are:
|
||||
- *Worst-Case Time:* `O(n * log n)`
|
||||
- *Best-Case Time:* `O(n)`
|
||||
- *Average Time:* `O(n * log n)`
|
||||
- *Worst-Case Space:* `O(n)` additional
|
||||
|
||||
? Implementation Note
|
||||
The sort implementation is based upon an adaptive, iterative mergesort
|
||||
that requires far fewer than `n * log(n)` comparisons when the vector
|
||||
is partially sorted. When the vector is randomly ordered, the
|
||||
performance is equivalent to a standard mergesort.
|
||||
|
||||
It takes equal advantage of ascending and descending runs in the array,
|
||||
making it much simpler to merge two or more sorted arrays: simply
|
||||
concatenate them and sort.
|
||||
|
||||
> Example
|
||||
Sorting a vector of numbers.
|
||||
[5, 2, 3, 45, 15].sort == [2, 3, 5, 15, 45]
|
||||
|
||||
> Example
|
||||
Sorting a vector of `Pair`s on the first element, descending.
|
||||
[Pair 1 2, Pair -1 8].sort (_.first) (order = Sort_Order.Descending)
|
||||
sort : (Any -> Any) -> (Any -> Any -> Ordering) -> Sort_Order -> Vector
|
||||
sort (on = x -> x) (by = (_.compare_to _)) (order = Sort_Order.Ascending) = ...
|
||||
```
|
||||
|
||||
Such documentation blocks describe:
|
||||
|
||||
- **Summary:** A basic summary of the behaviour of the method.
|
||||
- **Arguments:** Descriptions of each of the arguments to the function.
|
||||
- **Additional Information:** Additional exposition about the method.
|
||||
- **Note (Optional):** Optional notes containing potentially important
|
||||
information for more experienced users.
|
||||
- **Examples:** Examples of the method's usage, with descriptions.
|
||||
|
||||
In addition, a function will have a type signature that describes the expected
|
||||
types of the function arguments. It may also have defaults for its arguments,
|
||||
which will be shown in the
|
||||
|
||||
## General Libraries Notes
|
||||
|
||||
Some notes on the general structure of these libraries.
|
||||
|
||||
- All of these libraries are considered to be WIP as they are missing many
|
||||
pieces of functionality that they should have.
|
||||
- As the language doesn't currently have built-in support for access modifiers
|
||||
(e.g. `private`), so we instead use `PRIVATE` annotations at the top of
|
||||
documentation blocks. Any functionality annotated in such a form is not for
|
||||
public consumption.
|
||||
- The `Base.Meta` functionality is considered to be unstable as it is inherently
|
||||
tied to the internals of the compiler and the interpreter.
|
@ -111,6 +111,9 @@ state. The documentation syntax supports the following tags:
|
||||
of the library.
|
||||
- `REMOVED`: Used to describe constructs that have been removed and are no
|
||||
longer functional.
|
||||
- `PRIVATE`: Used to describe constructs that are private in the language.
|
||||
- `ADVANCED`: Items that are _not_ private, but are for power users.
|
||||
- `TEXT_ONLY`: Items that do not apply to the graphical mode.
|
||||
|
||||
Tags are added at the _top_ of the documentation block, and may also be
|
||||
accompanied by a description. This description directly follows the tag
|
||||
|
@ -48,6 +48,8 @@ public final class Language extends TruffleLanguage<Context> {
|
||||
/**
|
||||
* Creates a new Enso context.
|
||||
*
|
||||
* <p>This method is meant to be fast, and hence should not perform any long-running logic.
|
||||
*
|
||||
* @param env the language execution environment
|
||||
* @return a new Enso context
|
||||
*/
|
||||
@ -62,6 +64,16 @@ public final class Language extends TruffleLanguage<Context> {
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the context.
|
||||
*
|
||||
* @param context the language context
|
||||
*/
|
||||
@Override
|
||||
protected void initializeContext(Context context) throws Exception {
|
||||
context.initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this Enso execution environment is accessible in a multithreaded context.
|
||||
*
|
||||
|
@ -11,20 +11,19 @@ import org.enso.interpreter.dsl.Suspend;
|
||||
import org.enso.interpreter.node.BaseNode;
|
||||
import org.enso.interpreter.node.callable.thunk.ThunkExecutorNode;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
import org.enso.interpreter.runtime.callable.argument.Thunk;
|
||||
import org.enso.interpreter.runtime.error.PanicException;
|
||||
import org.enso.interpreter.runtime.error.RuntimeError;
|
||||
import org.enso.interpreter.runtime.state.Stateful;
|
||||
|
||||
@BuiltinMethod(
|
||||
type = "Panic",
|
||||
name = "catch",
|
||||
name = "recover",
|
||||
description = "Executes an action and converts any Panic thrown by it into an Error")
|
||||
public abstract class CatchPanicNode extends Node {
|
||||
public abstract class RecoverPanicNode extends Node {
|
||||
private @Child ThunkExecutorNode thunkExecutorNode = ThunkExecutorNode.build();
|
||||
|
||||
static CatchPanicNode build() {
|
||||
return CatchPanicNodeGen.create();
|
||||
static RecoverPanicNode build() {
|
||||
return RecoverPanicNodeGen.create();
|
||||
}
|
||||
|
||||
abstract Stateful execute(@MonadicState Object state, Object _this, @Suspend Object action);
|
@ -24,19 +24,19 @@ public abstract class CopyNode extends Node {
|
||||
}
|
||||
|
||||
abstract Object execute(
|
||||
Object _this, Object src, long source_index, Array that, long dest_index, long count);
|
||||
Object _this, Object src, long source_index, Array dest, long dest_index, long count);
|
||||
|
||||
@Specialization
|
||||
Object doArray(
|
||||
Object _this,
|
||||
Array src,
|
||||
long source_index,
|
||||
Array that,
|
||||
Array dest,
|
||||
long dest_index,
|
||||
long count,
|
||||
@CachedContext(Language.class) Context ctx) {
|
||||
System.arraycopy(
|
||||
src.getItems(), (int) source_index, that.getItems(), (int) dest_index, (int) count);
|
||||
src.getItems(), (int) source_index, dest.getItems(), (int) dest_index, (int) count);
|
||||
return ctx.getBuiltins().nothing().newInstance();
|
||||
}
|
||||
|
||||
@ -45,14 +45,14 @@ public abstract class CopyNode extends Node {
|
||||
Object _this,
|
||||
Object src,
|
||||
long source_index,
|
||||
Array that,
|
||||
Array dest,
|
||||
long dest_index,
|
||||
long count,
|
||||
@CachedLibrary(limit = "3") InteropLibrary arrays,
|
||||
@CachedContext(Language.class) Context ctx) {
|
||||
try {
|
||||
for (int i = 0; i < count; i++) {
|
||||
that.getItems()[(int) dest_index + i] = arrays.readArrayElement(src, source_index + i);
|
||||
dest.getItems()[(int) dest_index + i] = arrays.readArrayElement(src, source_index + i);
|
||||
}
|
||||
} catch (UnsupportedMessageException e) {
|
||||
throw new IllegalStateException("Unreachable");
|
||||
@ -65,7 +65,7 @@ public abstract class CopyNode extends Node {
|
||||
|
||||
@Fallback
|
||||
Object doOther(
|
||||
Object _this, Object src, long source_index, Array that, long dest_index, long count) {
|
||||
Object _this, Object src, long source_index, Array dest, long dest_index, long count) {
|
||||
Builtins builtins = lookupContextReference(Language.class).get().getBuiltins();
|
||||
throw new PanicException(
|
||||
builtins.error().makeTypeError(builtins.mutable().array().newInstance(), src), this);
|
||||
|
@ -5,7 +5,7 @@ import org.enso.interpreter.dsl.BuiltinMethod;
|
||||
import org.enso.interpreter.runtime.data.Array;
|
||||
import org.enso.interpreter.runtime.data.Ref;
|
||||
|
||||
@BuiltinMethod(type = "Ref", name = "new", description = "Creates an empty ref.")
|
||||
@BuiltinMethod(type = "Ref", name = "new", description = "Creates a new ref.")
|
||||
public class NewRefNode extends Node {
|
||||
|
||||
Object execute(Object _this, Object value) {
|
||||
|
@ -1,14 +1,33 @@
|
||||
package org.enso.interpreter.node.expression.builtin.runtime;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.dsl.Cached;
|
||||
import com.oracle.truffle.api.dsl.CachedContext;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.dsl.BuiltinMethod;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
import org.enso.interpreter.runtime.callable.atom.Atom;
|
||||
|
||||
@BuiltinMethod(type = "Runtime", name = "gc", description = "Forces garbage collection")
|
||||
public class GCNode extends Node {
|
||||
public abstract class GCNode extends Node {
|
||||
|
||||
public abstract Object execute(Object _this);
|
||||
|
||||
/** @return A new GCNode. */
|
||||
public static GCNode build() {
|
||||
return GCNodeGen.create();
|
||||
}
|
||||
|
||||
@Specialization
|
||||
Object doGc(Object _this, @CachedContext(Language.class) Context context) {
|
||||
runGC();
|
||||
return context.getBuiltins().nothing().newInstance();
|
||||
}
|
||||
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
Object execute(Object _this) {
|
||||
private void runGC() {
|
||||
System.gc();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ import org.enso.interpreter.node.expression.builtin.text.util.ToJavaStringNode;
|
||||
import org.enso.interpreter.runtime.data.text.Text;
|
||||
|
||||
@BuiltinMethod(
|
||||
type = "Prim_Text_Helpers",
|
||||
type = "Prim_Text_Helper",
|
||||
name = "optimize",
|
||||
description = "Forces flattening of a text value, for testing or purposes.")
|
||||
public class OptimizeNode extends Node {
|
||||
|
@ -24,7 +24,7 @@ public class EnsoProjectNode extends RootNode {
|
||||
if (pkgOpt.isPresent()) {
|
||||
Package<TruffleFile> pkg = pkgOpt.get();
|
||||
Object rootPath = context.getEnvironment().asGuestValue(new EnsoFile(pkg.root().normalize()));
|
||||
result = context.getBuiltins().getEnsoProject().newInstance(rootPath);
|
||||
result = context.getBuiltins().getProjectDescription().newInstance(rootPath);
|
||||
} else {
|
||||
result =
|
||||
new RuntimeError(context.getBuiltins().error().moduleNotInPackageError().newInstance());
|
||||
|
@ -1,8 +1,19 @@
|
||||
package org.enso.interpreter.runtime;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
|
||||
import com.oracle.truffle.api.TruffleFile;
|
||||
import com.oracle.truffle.api.TruffleLanguage;
|
||||
import com.oracle.truffle.api.TruffleLanguage.Env;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import org.enso.compiler.Compiler;
|
||||
import org.enso.home.HomeManager;
|
||||
import org.enso.interpreter.Language;
|
||||
@ -17,13 +28,6 @@ import org.enso.pkg.PackageManager;
|
||||
import org.enso.pkg.QualifiedName;
|
||||
import org.enso.polyglot.RuntimeOptions;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* The language context is the internal state of the language that is associated with each thread in
|
||||
* a running Enso program.
|
||||
@ -37,11 +41,13 @@ public class Context {
|
||||
private final PrintStream err;
|
||||
private final InputStream in;
|
||||
private final BufferedReader inReader;
|
||||
private final List<Package<TruffleFile>> packages;
|
||||
private final TopLevelScope topScope;
|
||||
private List<Package<TruffleFile>> packages;
|
||||
private @CompilationFinal TopLevelScope topScope;
|
||||
private final ThreadManager threadManager;
|
||||
private final ResourceManager resourceManager;
|
||||
private final boolean isCachingDisabled;
|
||||
private final Builtins builtins;
|
||||
private final String home;
|
||||
|
||||
/**
|
||||
* Creates a new Enso context.
|
||||
@ -59,8 +65,18 @@ public class Context {
|
||||
this.threadManager = new ThreadManager();
|
||||
this.resourceManager = new ResourceManager(this);
|
||||
this.isCachingDisabled = environment.getOptions().get(RuntimeOptions.DISABLE_INLINE_CACHES_KEY);
|
||||
TruffleFileSystem fs = new TruffleFileSystem();
|
||||
this.home = home;
|
||||
|
||||
builtins = new Builtins(this);
|
||||
|
||||
this.compiler = new Compiler(this, builtins);
|
||||
}
|
||||
|
||||
/** Perform expensive initialization logic for the context. */
|
||||
public void initialize() {
|
||||
this.getCompiler().initializeBuiltinsIr();
|
||||
|
||||
TruffleFileSystem fs = new TruffleFileSystem();
|
||||
packages = new ArrayList<>();
|
||||
|
||||
if (home != null) {
|
||||
@ -94,9 +110,8 @@ public class Context {
|
||||
Collectors.toMap(
|
||||
srcFile -> srcFile.qualifiedName().toString(),
|
||||
srcFile -> new Module(srcFile.qualifiedName(), srcFile.file())));
|
||||
topScope = new TopLevelScope(new Builtins(this), knownFiles);
|
||||
|
||||
this.compiler = new Compiler(this);
|
||||
topScope = new TopLevelScope(builtins, knownFiles);
|
||||
}
|
||||
|
||||
public TruffleFile getTruffleFile(File file) {
|
||||
|
@ -1,10 +1,17 @@
|
||||
package org.enso.interpreter.runtime.builtin;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Objects;
|
||||
import org.enso.compiler.Passes;
|
||||
import org.enso.compiler.context.FreshNameSupply;
|
||||
import org.enso.compiler.exception.CompilerError;
|
||||
import org.enso.compiler.phase.BuiltinsIrBuilder;
|
||||
import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.node.expression.builtin.debug.DebugBreakpointMethodGen;
|
||||
import org.enso.interpreter.node.expression.builtin.debug.DebugEvalMethodGen;
|
||||
import org.enso.interpreter.node.expression.builtin.error.CatchErrorMethodGen;
|
||||
import org.enso.interpreter.node.expression.builtin.error.CatchPanicMethodGen;
|
||||
import org.enso.interpreter.node.expression.builtin.error.RecoverPanicMethodGen;
|
||||
import org.enso.interpreter.node.expression.builtin.error.ThrowErrorMethodGen;
|
||||
import org.enso.interpreter.node.expression.builtin.error.ThrowPanicMethodGen;
|
||||
import org.enso.interpreter.node.expression.builtin.function.ApplicationOperatorMethodGen;
|
||||
@ -29,6 +36,7 @@ import org.enso.pkg.QualifiedName;
|
||||
|
||||
/** Container class for static predefined atoms, methods, and their containing scope. */
|
||||
public class Builtins {
|
||||
public static final String SOURCE_NAME = "Builtins.enso";
|
||||
public static final String MODULE_NAME = "Builtins.Main";
|
||||
|
||||
/** Container for method names needed outside this class. */
|
||||
@ -40,7 +48,7 @@ public class Builtins {
|
||||
|
||||
private final AtomConstructor any;
|
||||
private final AtomConstructor debug;
|
||||
private final AtomConstructor ensoProject;
|
||||
private final AtomConstructor projectDescription;
|
||||
private final AtomConstructor function;
|
||||
private final AtomConstructor nothing;
|
||||
|
||||
@ -70,8 +78,8 @@ public class Builtins {
|
||||
any = new AtomConstructor("Any", scope).initializeFields();
|
||||
bool = new Bool(language, scope);
|
||||
debug = new AtomConstructor("Debug", scope).initializeFields();
|
||||
ensoProject =
|
||||
new AtomConstructor("Enso_Project", scope)
|
||||
projectDescription =
|
||||
new AtomConstructor("Project_Description", scope)
|
||||
.initializeFields(
|
||||
new ArgumentDefinition(
|
||||
0, "prim_root_file", ArgumentDefinition.ExecutionMode.EXECUTE));
|
||||
@ -116,7 +124,7 @@ public class Builtins {
|
||||
scope.registerConstructor(error);
|
||||
scope.registerConstructor(state);
|
||||
scope.registerConstructor(debug);
|
||||
scope.registerConstructor(ensoProject);
|
||||
scope.registerConstructor(projectDescription);
|
||||
scope.registerConstructor(runtime);
|
||||
|
||||
scope.registerConstructor(java);
|
||||
@ -135,7 +143,7 @@ public class Builtins {
|
||||
scope.registerMethod(runtime, "gc", GCMethodGen.makeFunction(language));
|
||||
|
||||
scope.registerMethod(panic, "throw", ThrowPanicMethodGen.makeFunction(language));
|
||||
scope.registerMethod(panic, "recover", CatchPanicMethodGen.makeFunction(language));
|
||||
scope.registerMethod(panic, "recover", RecoverPanicMethodGen.makeFunction(language));
|
||||
scope.registerMethod(error, "throw", ThrowErrorMethodGen.makeFunction(language));
|
||||
scope.registerMethod(any, "catch", CatchErrorMethodGen.makeFunction(language));
|
||||
|
||||
@ -158,8 +166,31 @@ public class Builtins {
|
||||
thread, "with_interrupt_handler", WithInterruptHandlerMethodGen.makeFunction(language));
|
||||
|
||||
scope.registerMethod(unsafe, "set_atom_field", SetAtomFieldMethodGen.makeFunction(language));
|
||||
}
|
||||
|
||||
module.unsafeBuildIrStub();
|
||||
/** @return {@code true} if the IR has been initialized, otherwise {@code false} */
|
||||
public boolean isIrInitialized() {
|
||||
return this.module.getIr() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the IR for the builtins module from the builtins source file.
|
||||
*
|
||||
* @param freshNameSupply the compiler's fresh name supply
|
||||
* @param passes the passes manager for the compiler
|
||||
*/
|
||||
public void initializedBuiltinsIr(FreshNameSupply freshNameSupply, Passes passes) {
|
||||
try {
|
||||
var builtinsModuleBytes =
|
||||
Objects.requireNonNull(
|
||||
getClass().getClassLoader().getResourceAsStream(Builtins.SOURCE_NAME))
|
||||
.readAllBytes();
|
||||
String source = new String(builtinsModuleBytes, StandardCharsets.UTF_8);
|
||||
module.setLiteralSource(source);
|
||||
BuiltinsIrBuilder.build(module, freshNameSupply, passes);
|
||||
} catch (IOException e) {
|
||||
throw new CompilerError("Fatal, unable to read Builtins source file.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -227,8 +258,8 @@ public class Builtins {
|
||||
}
|
||||
|
||||
/** @return the {@code Enso_Project} atom constructor */
|
||||
public AtomConstructor getEnsoProject() {
|
||||
return ensoProject;
|
||||
public AtomConstructor getProjectDescription() {
|
||||
return projectDescription;
|
||||
}
|
||||
|
||||
/** @return the {@code System} atom constructor. */
|
||||
|
1778
engine/runtime/src/main/resources/Builtins.enso
Normal file
1778
engine/runtime/src/main/resources/Builtins.enso
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,6 @@
|
||||
package org.enso.compiler
|
||||
|
||||
import java.io.StringReader
|
||||
|
||||
import com.oracle.truffle.api.source.Source
|
||||
import org.enso.compiler.codegen.{AstToIr, IrToTruffle, RuntimeStubsGenerator}
|
||||
import org.enso.compiler.context.{FreshNameSupply, InlineContext, ModuleContext}
|
||||
@ -19,6 +18,7 @@ import org.enso.interpreter.node.{ExpressionNode => RuntimeExpression}
|
||||
import org.enso.interpreter.runtime.Context
|
||||
import org.enso.interpreter.runtime.error.ModuleDoesNotExistException
|
||||
import org.enso.interpreter.runtime.Module
|
||||
import org.enso.interpreter.runtime.builtin.Builtins
|
||||
import org.enso.interpreter.runtime.scope.{LocalScope, ModuleScope}
|
||||
import org.enso.polyglot.LanguageInfo
|
||||
import org.enso.syntax.text.Parser.IDMap
|
||||
@ -32,7 +32,7 @@ import scala.jdk.OptionConverters._
|
||||
*
|
||||
* @param context the language context
|
||||
*/
|
||||
class Compiler(val context: Context) {
|
||||
class Compiler(val context: Context, private val builtins: Builtins) {
|
||||
private val freshNameSupply: FreshNameSupply = new FreshNameSupply
|
||||
private val passes: Passes = new Passes
|
||||
private val passManager: PassManager = passes.passManager
|
||||
@ -40,6 +40,14 @@ class Compiler(val context: Context) {
|
||||
private val stubsGenerator: RuntimeStubsGenerator =
|
||||
new RuntimeStubsGenerator()
|
||||
|
||||
/** Lazy-initializes the IR for the builtins module.
|
||||
*/
|
||||
def initializeBuiltinsIr(): Unit = {
|
||||
if (!builtins.isIrInitialized) {
|
||||
builtins.initializedBuiltinsIr(freshNameSupply, passes)
|
||||
}
|
||||
}
|
||||
|
||||
/** Processes the provided language sources, registering any bindings in the
|
||||
* given scope.
|
||||
*
|
||||
|
@ -21,6 +21,7 @@ class Passes(passes: Option[List[PassGroup]] = None) {
|
||||
|
||||
val moduleDiscoveryPasses = new PassGroup(
|
||||
List(
|
||||
ModuleAnnotations,
|
||||
DocumentationComments,
|
||||
MainImportAndExport,
|
||||
ComplexType,
|
||||
@ -43,7 +44,7 @@ class Passes(passes: Option[List[PassGroup]] = None) {
|
||||
IgnoredBindings,
|
||||
TypeFunctions,
|
||||
TypeSignatures,
|
||||
Annotations,
|
||||
ExpressionAnnotations,
|
||||
AliasAnalysis,
|
||||
UppercaseNames,
|
||||
VectorLiterals,
|
||||
|
@ -10,7 +10,13 @@ import org.enso.compiler.core.IR.Name.MethodReference
|
||||
import org.enso.compiler.core.IR._
|
||||
import org.enso.compiler.exception.UnhandledEntity
|
||||
import org.enso.syntax.text.AST
|
||||
import org.enso.syntax.text.Shape.{SegmentEscape, SegmentExpr, SegmentPlain, SegmentRawEscape, TextUnclosed}
|
||||
import org.enso.syntax.text.Shape.{
|
||||
SegmentEscape,
|
||||
SegmentExpr,
|
||||
SegmentPlain,
|
||||
SegmentRawEscape,
|
||||
TextUnclosed
|
||||
}
|
||||
import org.enso.syntax.text.ast.text.Escape
|
||||
import org.enso.syntax.text.ast.text.Escape.Unicode
|
||||
|
||||
@ -126,6 +132,8 @@ object AstToIr {
|
||||
*/
|
||||
def translateModuleSymbol(inputAst: AST): Module.Scope.Definition = {
|
||||
inputAst match {
|
||||
case AST.Ident.Annotation.any(annotation) =>
|
||||
IR.Name.Annotation(annotation.name, getIdentifiedLocation(annotation))
|
||||
case AstView.Atom(consName, args) =>
|
||||
Module.Scope.Definition
|
||||
.Atom(
|
||||
@ -136,9 +144,9 @@ object AstToIr {
|
||||
case AstView.TypeDef(typeName, args, body) =>
|
||||
val translatedBody = translateTypeBody(body)
|
||||
val containsAtomDefOrInclude = translatedBody.exists {
|
||||
case _: IR.Module.Scope.Definition.Atom => true
|
||||
case _: IR.Name.Literal => true
|
||||
case _ => false
|
||||
case _: IR.Module.Scope.Definition.Atom => true
|
||||
case _: IR.Name.Literal => true
|
||||
case _ => false
|
||||
}
|
||||
val hasArgs = args.nonEmpty
|
||||
|
||||
@ -271,6 +279,8 @@ object AstToIr {
|
||||
.getOrElse(maybeParensedInput)
|
||||
|
||||
inputAst match {
|
||||
case AST.Ident.Annotation.any(ann) =>
|
||||
IR.Name.Annotation(ann.name, getIdentifiedLocation(ann))
|
||||
case AST.Ident.Cons.any(include) => translateIdent(include)
|
||||
case atom @ AstView.Atom(_, _) => translateModuleSymbol(atom)
|
||||
case fs @ AstView.FunctionSugar(_, _, _) => translateExpression(fs)
|
||||
|
@ -1,7 +1,5 @@
|
||||
package org.enso.compiler.codegen
|
||||
|
||||
import java.math.BigInteger
|
||||
|
||||
import com.oracle.truffle.api.Truffle
|
||||
import com.oracle.truffle.api.source.{Source, SourceSection}
|
||||
import org.enso.compiler.core.IR
|
||||
@ -19,7 +17,6 @@ import org.enso.compiler.pass.analyse.{
|
||||
}
|
||||
import org.enso.compiler.pass.optimise.ApplicationSaturation
|
||||
import org.enso.compiler.pass.resolve.{
|
||||
Annotations,
|
||||
MethodDefinitions,
|
||||
Patterns,
|
||||
UppercaseNames
|
||||
@ -37,13 +34,7 @@ import org.enso.interpreter.node.callable.{
|
||||
}
|
||||
import org.enso.interpreter.node.controlflow.caseexpr._
|
||||
import org.enso.interpreter.node.expression.atom.QualifiedAccessorNode
|
||||
import org.enso.interpreter.node.expression.constant.{
|
||||
ConstantObjectNode,
|
||||
ConstructorNode,
|
||||
DynamicSymbolNode,
|
||||
EnsoProjectNode,
|
||||
ErrorNode
|
||||
}
|
||||
import org.enso.interpreter.node.expression.constant._
|
||||
import org.enso.interpreter.node.expression.literal.{
|
||||
BigIntegerLiteralNode,
|
||||
DecimalLiteralNode,
|
||||
@ -73,6 +64,7 @@ import org.enso.interpreter.runtime.error.DuplicateArgumentNameException
|
||||
import org.enso.interpreter.runtime.scope.{LocalScope, ModuleScope}
|
||||
import org.enso.interpreter.{Constants, Language}
|
||||
|
||||
import java.math.BigInteger
|
||||
import scala.collection.mutable
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
|
||||
@ -304,7 +296,7 @@ class IrToTruffle(
|
||||
): BaseNode.TailStatus = {
|
||||
val isTailPosition =
|
||||
expression.getMetadata(TailCall).contains(TailCall.TailPosition.Tail)
|
||||
val isTailAnnotated = expression.getMetadata(Annotations).isDefined
|
||||
val isTailAnnotated = TailCall.isTailAnnotated(expression)
|
||||
if (isTailPosition) {
|
||||
if (isTailAnnotated) {
|
||||
BaseNode.TailStatus.TAIL_LOOP
|
||||
|
@ -20,7 +20,8 @@ case class InlineContext(
|
||||
localScope: Option[LocalScope] = None,
|
||||
isInTailPosition: Option[Boolean] = None,
|
||||
freshNameSupply: Option[FreshNameSupply] = None,
|
||||
passConfiguration: Option[PassConfiguration] = None
|
||||
passConfiguration: Option[PassConfiguration] = None,
|
||||
noWarnings: Boolean = false
|
||||
)
|
||||
object InlineContext {
|
||||
|
||||
|
@ -12,5 +12,6 @@ import org.enso.interpreter.runtime.Module
|
||||
case class ModuleContext(
|
||||
module: Module,
|
||||
freshNameSupply: Option[FreshNameSupply] = None,
|
||||
passConfiguration: Option[PassConfiguration] = None
|
||||
passConfiguration: Option[PassConfiguration] = None,
|
||||
noWarnings: Boolean = false
|
||||
)
|
||||
|
@ -2093,7 +2093,9 @@ object IR {
|
||||
override val location: Option[IdentifiedLocation],
|
||||
override val passData: MetadataStorage = MetadataStorage(),
|
||||
override val diagnostics: DiagnosticStorage = DiagnosticStorage()
|
||||
) extends Name {
|
||||
) extends Name
|
||||
with IR.Module.Scope.Definition
|
||||
with IRKind.Primitive {
|
||||
override protected var id: Identifier = randomId
|
||||
|
||||
/** Creates a copy of `this`.
|
||||
@ -5433,10 +5435,10 @@ object IR {
|
||||
/** An error coming from a tail call annotation placed in a syntactically
|
||||
* incorrect position.
|
||||
*/
|
||||
case object UnexpectedTailCallAnnotation extends Reason {
|
||||
case object UnexpectedAnnotation extends Reason {
|
||||
override def explain(originalName: Name): String =
|
||||
s"Unexpected @TailCall annotation. This annotation can only be " +
|
||||
s"used with function applications."
|
||||
s"Unexpected ${originalName.name} annotation. This annotation can " +
|
||||
s"only be used with function applications."
|
||||
}
|
||||
|
||||
/** An error coming from an unexpected occurence of a polyglot symbol.
|
||||
@ -5699,7 +5701,8 @@ object IR {
|
||||
s"$base is not a valid numeric base."
|
||||
}
|
||||
|
||||
case class InvalidNumberForBase(base: String, number: String) extends Reason {
|
||||
case class InvalidNumberForBase(base: String, number: String)
|
||||
extends Reason {
|
||||
override def explanation: String =
|
||||
s"$number is not valid in $base."
|
||||
}
|
||||
|
@ -175,6 +175,11 @@ case object AliasAnalysis extends IRPass {
|
||||
"Type signatures should not exist at the top level during " +
|
||||
"alias analysis."
|
||||
)
|
||||
case _: IR.Name.Annotation =>
|
||||
throw new CompilerError(
|
||||
"Annotations should already be associated by the point of alias " +
|
||||
"analysis."
|
||||
)
|
||||
case err: IR.Error => err
|
||||
}
|
||||
}
|
||||
|
@ -117,6 +117,11 @@ case object CachePreferenceAnalysis extends IRPass {
|
||||
"Type signatures should not exist at the top level during " +
|
||||
"cache preference analysis."
|
||||
)
|
||||
case _: IR.Name.Annotation =>
|
||||
throw new CompilerError(
|
||||
"Annotations should already be associated by the point of " +
|
||||
"cache preference analysis."
|
||||
)
|
||||
case err: IR.Error => err
|
||||
}
|
||||
|
||||
|
@ -126,6 +126,11 @@ case object DataflowAnalysis extends IRPass {
|
||||
"Type signatures should not exist at the top level during " +
|
||||
"dataflow analysis."
|
||||
)
|
||||
case _: IR.Name.Annotation =>
|
||||
throw new CompilerError(
|
||||
"Annotations should already be associated by the point of " +
|
||||
"dataflow analysis."
|
||||
)
|
||||
case err: IR.Error => err
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ import org.enso.compiler.core.ir.MetadataStorage._
|
||||
import org.enso.compiler.exception.CompilerError
|
||||
import org.enso.compiler.pass.IRPass
|
||||
import org.enso.compiler.pass.desugar._
|
||||
import org.enso.compiler.pass.resolve.Annotations
|
||||
import org.enso.compiler.pass.resolve.ExpressionAnnotations
|
||||
|
||||
/** This pass performs tail call analysis on the Enso IR.
|
||||
*
|
||||
@ -114,6 +114,11 @@ case object TailCall extends IRPass {
|
||||
"Type signatures should not exist at the top level during " +
|
||||
"tail call analysis."
|
||||
)
|
||||
case _: IR.Name.Annotation =>
|
||||
throw new CompilerError(
|
||||
"Annotations should already be associated by the point of " +
|
||||
"tail call analysis."
|
||||
)
|
||||
case err: IR.Error => err
|
||||
}
|
||||
}
|
||||
@ -130,11 +135,8 @@ case object TailCall extends IRPass {
|
||||
isInTailPosition: Boolean
|
||||
): IR.Expression = {
|
||||
val expressionWithWarning =
|
||||
if (
|
||||
expression
|
||||
.getMetadata(Annotations)
|
||||
.contains(Annotations.TailCallAnnotated) && !isInTailPosition
|
||||
) expression.addDiagnostic(IR.Warning.WrongTco(expression.location))
|
||||
if (isTailAnnotated(expression) && !isInTailPosition)
|
||||
expression.addDiagnostic(IR.Warning.WrongTco(expression.location))
|
||||
else expression
|
||||
expressionWithWarning match {
|
||||
case empty: IR.Empty =>
|
||||
@ -480,4 +482,21 @@ case object TailCall extends IRPass {
|
||||
tailPosition.isTail
|
||||
}
|
||||
}
|
||||
|
||||
/** Checks if the provided `expression` is annotated with a tail call
|
||||
* annotation.
|
||||
*
|
||||
* @param expression the expression to check
|
||||
* @return `true` if `expression` is annotated with `@Tail_Call`, otherwise
|
||||
* `false`
|
||||
*/
|
||||
def isTailAnnotated(expression: IR.Expression): Boolean = {
|
||||
expression
|
||||
.getMetadata(ExpressionAnnotations)
|
||||
.exists(anns =>
|
||||
anns.annotations.exists(a =>
|
||||
a.name == ExpressionAnnotations.tailCallName
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,8 @@ import org.enso.compiler.pass.optimise.{
|
||||
ApplicationSaturation,
|
||||
LambdaConsolidate
|
||||
}
|
||||
import org.enso.compiler.pass.resolve.IgnoredBindings
|
||||
import org.enso.compiler.pass.resolve.{IgnoredBindings, ModuleAnnotations}
|
||||
import org.enso.compiler.core.ir.MetadataStorage._
|
||||
|
||||
import scala.annotation.unused
|
||||
|
||||
@ -41,7 +42,7 @@ case object ComplexType extends IRPass {
|
||||
override type Metadata = IRPass.Metadata.Empty
|
||||
override type Config = IRPass.Configuration.Default
|
||||
|
||||
override val precursorPasses: Seq[IRPass] = List()
|
||||
override val precursorPasses: Seq[IRPass] = List(ModuleAnnotations)
|
||||
override val invalidatedPasses: Seq[IRPass] =
|
||||
List(
|
||||
AliasAnalysis,
|
||||
@ -102,12 +103,15 @@ case object ComplexType extends IRPass {
|
||||
def desugarComplexType(
|
||||
typ: IR.Module.Scope.Definition.Type
|
||||
): List[IR.Module.Scope.Definition] = {
|
||||
val atomDefs = typ.body.collect { case d: IR.Module.Scope.Definition.Atom =>
|
||||
d
|
||||
}
|
||||
val atomIncludes = typ.body.collect { case n: IR.Name =>
|
||||
n
|
||||
}
|
||||
val annotations = typ.getMetadata(ModuleAnnotations)
|
||||
val atomDefs = typ.body
|
||||
.collect { case d: IR.Module.Scope.Definition.Atom => d }
|
||||
.map(atom =>
|
||||
annotations
|
||||
.map(ann => atom.updateMetadata(ModuleAnnotations -->> ann))
|
||||
.getOrElse(atom)
|
||||
)
|
||||
val atomIncludes = typ.body.collect { case n: IR.Name => n }
|
||||
val namesToDefineMethodsOn = atomIncludes ++ atomDefs.map(_.name)
|
||||
|
||||
val remainingEntities = typ.body.filterNot {
|
||||
|
@ -148,6 +148,11 @@ case object FunctionBinding extends IRPass {
|
||||
"Documentation should not be present during function binding" +
|
||||
"desugaring."
|
||||
)
|
||||
case _: IR.Name.Annotation =>
|
||||
throw new CompilerError(
|
||||
"Annotations should already be associated by the point of " +
|
||||
"function binding desugaring."
|
||||
)
|
||||
case a: IR.Type.Ascription => a
|
||||
case e: IR.Error => e
|
||||
}
|
||||
|
@ -46,8 +46,9 @@ case object UnusedBindings extends IRPass {
|
||||
override def runModule(
|
||||
ir: IR.Module,
|
||||
moduleContext: ModuleContext
|
||||
): IR.Module =
|
||||
): IR.Module = if (!moduleContext.noWarnings) {
|
||||
ir.mapExpressions(runExpression(_, InlineContext(moduleContext.module)))
|
||||
} else ir
|
||||
|
||||
/** Lints an arbitrary expression.
|
||||
*
|
||||
@ -60,12 +61,13 @@ case object UnusedBindings extends IRPass {
|
||||
override def runExpression(
|
||||
ir: IR.Expression,
|
||||
inlineContext: InlineContext
|
||||
): IR.Expression =
|
||||
): IR.Expression = if (!inlineContext.noWarnings) {
|
||||
ir.transformExpressions {
|
||||
case binding: IR.Expression.Binding => lintBinding(binding, inlineContext)
|
||||
case function: IR.Function => lintFunction(function, inlineContext)
|
||||
case cse: IR.Case => lintCase(cse, inlineContext)
|
||||
}
|
||||
} else ir
|
||||
|
||||
// === Pass Internals =======================================================
|
||||
|
||||
|
@ -4,6 +4,7 @@ import org.enso.compiler.context.{InlineContext, ModuleContext}
|
||||
import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.core.IR.Case.Branch
|
||||
import org.enso.compiler.core.ir.MetadataStorage._
|
||||
import org.enso.compiler.exception.CompilerError
|
||||
import org.enso.compiler.pass.IRPass
|
||||
import org.enso.compiler.pass.desugar.{ComplexType, GenerateMethodBodies}
|
||||
|
||||
@ -140,6 +141,11 @@ case object DocumentationComments extends IRPass {
|
||||
case doc: IR.Comment.Documentation => doc
|
||||
case tySig: IR.Type.Ascription => tySig
|
||||
case err: IR.Error => err
|
||||
case _: IR.Name.Annotation =>
|
||||
throw new CompilerError(
|
||||
"Annotations should already be associated by the point of " +
|
||||
"documentation comment resolution."
|
||||
)
|
||||
}
|
||||
|
||||
/** Resolves documentation comments in a module.
|
||||
|
@ -5,26 +5,25 @@ import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.core.ir.MetadataStorage.ToPair
|
||||
import org.enso.compiler.exception.CompilerError
|
||||
import org.enso.compiler.pass.IRPass
|
||||
import org.enso.compiler.pass.analyse.AliasAnalysis
|
||||
import org.enso.compiler.pass.resolve.ModuleAnnotations.Annotations
|
||||
|
||||
case object Annotations extends IRPass {
|
||||
case object TailCallAnnotated extends IRPass.Metadata {
|
||||
override val metadataName: String = "TailCallAnnotated"
|
||||
override def duplicate(): Option[IRPass.Metadata] = Some(this)
|
||||
}
|
||||
|
||||
val tailCallName = "@Tail_Call"
|
||||
case object ExpressionAnnotations extends IRPass {
|
||||
val tailCallName = "@Tail_Call"
|
||||
val builtinMethodName = "@Builtin_Method"
|
||||
val knownAnnotations = Seq(tailCallName, builtinMethodName)
|
||||
|
||||
/** The type of the metadata object that the pass writes to the IR. */
|
||||
override type Metadata = TailCallAnnotated.type
|
||||
override type Metadata = Annotations
|
||||
|
||||
/** 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()
|
||||
override val precursorPasses: Seq[IRPass] = Seq(ModuleAnnotations)
|
||||
|
||||
/** The passes that are invalidated by running this pass. */
|
||||
override val invalidatedPasses: Seq[IRPass] = Seq()
|
||||
override val invalidatedPasses: Seq[IRPass] = Seq(AliasAnalysis)
|
||||
|
||||
/** Executes the pass on the provided `ir`, and returns a possibly transformed
|
||||
* or annotated version of `ir`.
|
||||
@ -70,7 +69,7 @@ case object Annotations extends IRPass {
|
||||
_,
|
||||
_
|
||||
) =>
|
||||
if (ann.name == tailCallName) {
|
||||
if (isKnownAnnotation(ann.name)) {
|
||||
arguments match {
|
||||
case List() =>
|
||||
throw new CompilerError(
|
||||
@ -78,13 +77,13 @@ case object Annotations extends IRPass {
|
||||
)
|
||||
case List(arg) =>
|
||||
doExpression(arg.value)
|
||||
.updateMetadata(this -->> TailCallAnnotated)
|
||||
.updateMetadata(this -->> Annotations(Seq(ann)))
|
||||
case realFun :: args =>
|
||||
val recurFun = doExpression(realFun.value)
|
||||
val recurArgs = args.map(_.mapExpressions(doExpression))
|
||||
app
|
||||
.copy(function = recurFun, arguments = recurArgs)
|
||||
.updateMetadata(this -->> TailCallAnnotated)
|
||||
.updateMetadata(this -->> Annotations(Seq(ann)))
|
||||
}
|
||||
} else {
|
||||
val err =
|
||||
@ -92,14 +91,22 @@ case object Annotations extends IRPass {
|
||||
app.copy(function = err)
|
||||
}
|
||||
case ann: IR.Name.Annotation =>
|
||||
if (ann.name == tailCallName) {
|
||||
if (isKnownAnnotation(ann.name)) {
|
||||
IR.Error.Resolution(
|
||||
ann,
|
||||
IR.Error.Resolution.UnexpectedTailCallAnnotation
|
||||
IR.Error.Resolution.UnexpectedAnnotation
|
||||
)
|
||||
} else {
|
||||
IR.Error.Resolution(ann, IR.Error.Resolution.UnknownAnnotation)
|
||||
}
|
||||
}
|
||||
|
||||
/** Checks if `name` is a known annotation.
|
||||
*
|
||||
* @param name the annotation name to check
|
||||
* @return `true` if `name` is a known annotation, otherwise `false`
|
||||
*/
|
||||
def isKnownAnnotation(name: String): Boolean = {
|
||||
knownAnnotations.contains(name)
|
||||
}
|
||||
}
|
@ -0,0 +1,123 @@
|
||||
package org.enso.compiler.pass.resolve
|
||||
|
||||
import org.enso.compiler.context.{InlineContext, ModuleContext}
|
||||
import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.core.IR.Module.Scope.Definition
|
||||
import org.enso.compiler.core.IR.Name
|
||||
import org.enso.compiler.core.ir.MetadataStorage._
|
||||
import org.enso.compiler.pass.IRPass
|
||||
import org.enso.compiler.pass.desugar.{
|
||||
ComplexType,
|
||||
FunctionBinding,
|
||||
GenerateMethodBodies
|
||||
}
|
||||
|
||||
import scala.annotation.unused
|
||||
|
||||
/** A pass responsible for the discovery of module annotations, and for
|
||||
* associating them with the corresponding construct.
|
||||
*/
|
||||
case object ModuleAnnotations extends IRPass {
|
||||
override type Metadata = Annotations
|
||||
override type Config = IRPass.Configuration.Default
|
||||
override val precursorPasses: Seq[IRPass] = Seq()
|
||||
override val invalidatedPasses: Seq[IRPass] = Seq(
|
||||
DocumentationComments,
|
||||
ComplexType,
|
||||
FunctionBinding,
|
||||
GenerateMethodBodies
|
||||
)
|
||||
|
||||
/** Resolves module-level annotations.
|
||||
*
|
||||
* @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 = {
|
||||
var lastAnnotations: Seq[IR.Name.Annotation] = Seq()
|
||||
val newBindings = for (binding <- ir.bindings) yield {
|
||||
binding match {
|
||||
case ann: Name.Annotation =>
|
||||
lastAnnotations :+= ann
|
||||
None
|
||||
case comment: IR.Comment => Some(comment)
|
||||
case typ: Definition.Type =>
|
||||
val res = Some(
|
||||
resolveComplexType(typ).updateMetadata(
|
||||
this -->> Annotations(lastAnnotations)
|
||||
)
|
||||
)
|
||||
lastAnnotations = Seq()
|
||||
res
|
||||
case entity =>
|
||||
val res = Some(
|
||||
entity.updateMetadata(this -->> Annotations(lastAnnotations))
|
||||
)
|
||||
lastAnnotations = Seq()
|
||||
res
|
||||
}
|
||||
}
|
||||
ir.copy(bindings = newBindings.flatten)
|
||||
}
|
||||
|
||||
/** Resolves top level annotations within a complex type.
|
||||
*
|
||||
* @param typ the type in which to resolve annotations
|
||||
* @return `typ` with all top-level annotations resolved
|
||||
*/
|
||||
def resolveComplexType(typ: Definition.Type): Definition.Type = {
|
||||
var lastAnnotations: Seq[IR.Name.Annotation] = Seq()
|
||||
val newBodyElems = typ.body.flatMap {
|
||||
case ann: Name.Annotation =>
|
||||
lastAnnotations :+= ann
|
||||
None
|
||||
case comment: IR.Comment => Some(comment)
|
||||
case entity =>
|
||||
val res = Some(
|
||||
entity.updateMetadata(this -->> Annotations(lastAnnotations))
|
||||
)
|
||||
lastAnnotations = Seq()
|
||||
res
|
||||
}
|
||||
typ.copy(body = newBodyElems)
|
||||
}
|
||||
|
||||
/** Execute the pass on an expression.
|
||||
*
|
||||
* As the pass only deals with module-level annotations this is a no-op.
|
||||
*
|
||||
* @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,
|
||||
@unused inlineContext: InlineContext
|
||||
): IR.Expression = ir
|
||||
|
||||
/** A container for annotations on an IR construct.
|
||||
*
|
||||
* @param annotations the initial annotations for the container
|
||||
*/
|
||||
case class Annotations(annotations: Seq[IR.Name.Annotation])
|
||||
extends IRPass.Metadata {
|
||||
override val metadataName: String = "Annotations"
|
||||
override def duplicate(): Option[IRPass.Metadata] = Some(this.copy())
|
||||
|
||||
/** Add an annotation to the annotations container.
|
||||
*
|
||||
* @param annotation the annotation to add
|
||||
* @return `this`, with `annotation` added to it
|
||||
*/
|
||||
def addAnnotation(annotation: IR.Name.Annotation): Annotations =
|
||||
this.copy(annotations = this.annotations :+ annotation)
|
||||
}
|
||||
}
|
@ -139,6 +139,11 @@ case object SuspendedArguments extends IRPass {
|
||||
throw new CompilerError("Type ascriptions should not be present.")
|
||||
case _: IR.Comment =>
|
||||
throw new CompilerError("Comments should not be present.")
|
||||
case _: IR.Name.Annotation =>
|
||||
throw new CompilerError(
|
||||
"Annotations should already be associated by the point of " +
|
||||
"suspended arguments analysis."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,8 @@ case object TypeSignatures extends IRPass {
|
||||
override type Config = IRPass.Configuration.Default
|
||||
|
||||
override val precursorPasses: Seq[IRPass] = List(
|
||||
TypeFunctions
|
||||
TypeFunctions,
|
||||
ModuleAnnotations,
|
||||
)
|
||||
override val invalidatedPasses: Seq[IRPass] = List(
|
||||
AliasAnalysis,
|
||||
@ -93,21 +94,34 @@ case object TypeSignatures extends IRPass {
|
||||
newMethod.updateMetadata(DocumentationComments -->> doc)
|
||||
)
|
||||
.getOrElse(newMethod)
|
||||
val newMethodWithAnnotations = asc
|
||||
.getMetadata(ModuleAnnotations)
|
||||
.map(annotations =>
|
||||
newMethodWithDoc.updateMetadata(
|
||||
ModuleAnnotations -->> annotations
|
||||
)
|
||||
)
|
||||
.getOrElse(newMethodWithDoc)
|
||||
|
||||
typed match {
|
||||
case ref: IR.Name.MethodReference =>
|
||||
if (ref isSameReferenceAs methodRef) {
|
||||
Some(
|
||||
newMethodWithDoc.updateMetadata(this -->> Signature(sig))
|
||||
newMethodWithAnnotations.updateMetadata(
|
||||
this -->> Signature(sig)
|
||||
)
|
||||
)
|
||||
} else {
|
||||
List(
|
||||
IR.Error.Unexpected.TypeSignature(asc),
|
||||
newMethodWithDoc
|
||||
newMethodWithAnnotations
|
||||
)
|
||||
}
|
||||
case _ =>
|
||||
List(IR.Error.Unexpected.TypeSignature(asc), newMethodWithDoc)
|
||||
List(
|
||||
IR.Error.Unexpected.TypeSignature(asc),
|
||||
newMethodWithAnnotations
|
||||
)
|
||||
}
|
||||
case None => Some(newMethod)
|
||||
}
|
||||
@ -122,6 +136,11 @@ case object TypeSignatures extends IRPass {
|
||||
"Complex type definitions should not be present during type " +
|
||||
"signature resolution."
|
||||
)
|
||||
case _: IR.Name.Annotation =>
|
||||
throw new CompilerError(
|
||||
"Annotations should already be associated by the point of " +
|
||||
"type signature resolution."
|
||||
)
|
||||
case _: IR.Comment.Documentation =>
|
||||
throw new CompilerError(
|
||||
"Documentation comments should not be present during type " +
|
||||
|
@ -0,0 +1,61 @@
|
||||
package org.enso.compiler.phase
|
||||
|
||||
import org.enso.compiler.Passes
|
||||
import org.enso.compiler.codegen.AstToIr
|
||||
import org.enso.compiler.context.{FreshNameSupply, ModuleContext}
|
||||
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.
|
||||
*/
|
||||
object BuiltinsIrBuilder {
|
||||
|
||||
/** Builds the IR for the builtins module based on the builtins source file.
|
||||
*
|
||||
* We guarantee that the builtins file neither imports anything or restricts
|
||||
* any exports, and are hence safe to unconditionally run most of the compiler
|
||||
* pipeline. We do not want to run codegen, however, as these definitions
|
||||
* would conflict with the existing builtins.
|
||||
*
|
||||
* This is kept as a separate flow as it is independent of the true
|
||||
* compilation pipeline
|
||||
*
|
||||
* @param module the module to build the IR for
|
||||
* @param freshNameSupply the compiler's fresh name supply
|
||||
* @param passes the compiler's pass manager
|
||||
*/
|
||||
def build(
|
||||
@unused module: Module,
|
||||
@unused freshNameSupply: FreshNameSupply,
|
||||
@unused passes: Passes
|
||||
): Unit = {
|
||||
val passManager = passes.passManager
|
||||
val moduleContext = ModuleContext(
|
||||
module = module,
|
||||
freshNameSupply = Some(freshNameSupply),
|
||||
noWarnings = true
|
||||
)
|
||||
val parsedAst = Parser().runWithIds(module.getSource.getCharacters.toString)
|
||||
val initialIr = AstToIr.translate(parsedAst)
|
||||
val irAfterModDiscovery = passManager.runPassesOnModule(
|
||||
initialIr,
|
||||
moduleContext,
|
||||
passes.moduleDiscoveryPasses
|
||||
)
|
||||
module.unsafeSetIr(irAfterModDiscovery)
|
||||
module.unsafeSetCompilationStage(Module.CompilationStage.AFTER_PARSING)
|
||||
|
||||
new ExportsResolution().run(List(module))
|
||||
val irAfterCompilation = passManager.runPassesOnModule(
|
||||
irAfterModDiscovery,
|
||||
moduleContext,
|
||||
passes.functionBodyPasses
|
||||
)
|
||||
module.unsafeSetIr(irAfterCompilation)
|
||||
module.unsafeSetCompilationStage(CompilationStage.AFTER_CODEGEN)
|
||||
}
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
package org.enso.compiler.phase
|
||||
|
||||
import org.enso.compiler.core.IR
|
||||
import org.enso.interpreter.runtime.Module
|
||||
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.pass.analyse.BindingAnalysis
|
||||
import org.enso.interpreter.runtime.Module
|
||||
|
||||
import scala.jdk.CollectionConverters._
|
||||
|
||||
|
@ -8,15 +8,7 @@ import org.enso.compiler.pass.analyse.{AliasAnalysis, BindingAnalysis}
|
||||
import org.enso.compiler.pass.desugar._
|
||||
import org.enso.compiler.pass.lint.ShadowedPatternFields
|
||||
import org.enso.compiler.pass.optimise.UnreachableMatchBranches
|
||||
import org.enso.compiler.pass.resolve.{
|
||||
Annotations,
|
||||
DocumentationComments,
|
||||
IgnoredBindings,
|
||||
MethodDefinitions,
|
||||
ModuleThisToHere,
|
||||
TypeFunctions,
|
||||
TypeSignatures
|
||||
}
|
||||
import org.enso.compiler.pass.resolve.{DocumentationComments, ExpressionAnnotations, IgnoredBindings, MethodDefinitions, ModuleAnnotations, ModuleThisToHere, TypeFunctions, TypeSignatures}
|
||||
|
||||
class PassesTest extends CompilerTest {
|
||||
|
||||
@ -48,6 +40,7 @@ class PassesTest extends CompilerTest {
|
||||
"get the precursors of a given pass" in {
|
||||
passes.getPrecursors(AliasAnalysis).map(_.passes) shouldEqual Some(
|
||||
List(
|
||||
ModuleAnnotations,
|
||||
DocumentationComments,
|
||||
MainImportAndExport,
|
||||
ComplexType,
|
||||
@ -65,7 +58,7 @@ class PassesTest extends CompilerTest {
|
||||
IgnoredBindings,
|
||||
TypeFunctions,
|
||||
TypeSignatures,
|
||||
Annotations
|
||||
ExpressionAnnotations
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -852,38 +852,70 @@ class AstToIrTest extends CompilerTest with Inside {
|
||||
}
|
||||
}
|
||||
|
||||
"properly support different kinds of imports" in {
|
||||
val imports = List(
|
||||
"import Foo.Bar as Baz",
|
||||
"import Foo.Bar",
|
||||
"from Foo.Bar import Baz",
|
||||
"from Foo.Bar import Baz, Spam",
|
||||
"from Foo.Bar import all",
|
||||
"from Foo.Bar as Eggs import all hiding Spam",
|
||||
"from Foo.Bar import all hiding Spam, Eggs"
|
||||
)
|
||||
imports
|
||||
.mkString("\n")
|
||||
.toIrModule
|
||||
.imports
|
||||
.map(_.showCode()) shouldEqual imports
|
||||
"AST translation of top-level annotations" should {
|
||||
"support annotations at the top level" in {
|
||||
val ir =
|
||||
"""@My_Annotation
|
||||
|type Foo a b
|
||||
|""".stripMargin.toIrModule
|
||||
|
||||
ir.bindings.head shouldBe an[IR.Name.Annotation]
|
||||
ir.bindings(1) shouldBe an[IR.Module.Scope.Definition.Atom]
|
||||
}
|
||||
|
||||
"support annotations inside complex type bodies" in {
|
||||
val ir =
|
||||
"""type My_Type
|
||||
| @My_Annotation
|
||||
| type Foo
|
||||
|
|
||||
| @My_Annotation
|
||||
| add a = this + a
|
||||
|""".stripMargin.toIrModule
|
||||
|
||||
ir.bindings.head shouldBe an[IR.Module.Scope.Definition.Type]
|
||||
val complexType =
|
||||
ir.bindings.head.asInstanceOf[IR.Module.Scope.Definition.Type]
|
||||
|
||||
complexType.body.head shouldBe an[IR.Name.Annotation]
|
||||
complexType.body(2) shouldBe an[IR.Name.Annotation]
|
||||
}
|
||||
}
|
||||
|
||||
"properly support different kinds of exports" in {
|
||||
val exports = List(
|
||||
"export Foo.Bar as Baz",
|
||||
"export Foo.Bar",
|
||||
"from Foo.Bar export Baz",
|
||||
"from Foo.Bar export baz, Spam",
|
||||
"from Foo.Bar export all",
|
||||
"from Foo.Bar as Eggs export all hiding Spam",
|
||||
"from Foo.Bar export all hiding Spam, eggs"
|
||||
)
|
||||
exports
|
||||
.mkString("\n")
|
||||
.toIrModule
|
||||
.exports
|
||||
.map(_.showCode()) shouldEqual exports
|
||||
"AST translation for imports and exports" should {
|
||||
"properly support different kinds of imports" in {
|
||||
val imports = List(
|
||||
"import Foo.Bar as Baz",
|
||||
"import Foo.Bar",
|
||||
"from Foo.Bar import Baz",
|
||||
"from Foo.Bar import Baz, Spam",
|
||||
"from Foo.Bar import all",
|
||||
"from Foo.Bar as Eggs import all hiding Spam",
|
||||
"from Foo.Bar import all hiding Spam, Eggs"
|
||||
)
|
||||
imports
|
||||
.mkString("\n")
|
||||
.toIrModule
|
||||
.imports
|
||||
.map(_.showCode()) shouldEqual imports
|
||||
}
|
||||
|
||||
"properly support different kinds of exports" in {
|
||||
val exports = List(
|
||||
"export Foo.Bar as Baz",
|
||||
"export Foo.Bar",
|
||||
"from Foo.Bar export Baz",
|
||||
"from Foo.Bar export baz, Spam",
|
||||
"from Foo.Bar export all",
|
||||
"from Foo.Bar as Eggs export all hiding Spam",
|
||||
"from Foo.Bar export all hiding Spam, eggs"
|
||||
)
|
||||
exports
|
||||
.mkString("\n")
|
||||
.toIrModule
|
||||
.exports
|
||||
.map(_.showCode()) shouldEqual exports
|
||||
}
|
||||
}
|
||||
|
||||
"AST translation of erroneous constructs" should {
|
||||
|
@ -5,6 +5,7 @@ import org.enso.compiler.context.ModuleContext
|
||||
import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.core.IR.Module.Scope.Definition
|
||||
import org.enso.compiler.pass.desugar.ComplexType
|
||||
import org.enso.compiler.pass.resolve.ModuleAnnotations
|
||||
import org.enso.compiler.pass.{PassConfiguration, PassGroup, PassManager}
|
||||
import org.enso.compiler.test.CompilerTest
|
||||
|
||||
@ -79,11 +80,28 @@ class ComplexTypeTest extends CompilerTest {
|
||||
| type Bar
|
||||
|""".stripMargin.preprocessModule.desugar
|
||||
|
||||
exactly(2, ir.bindings) shouldBe an[Definition.Atom]
|
||||
exactly(2, ir.bindings) shouldBe a[Definition.Atom]
|
||||
ir.bindings.head.asInstanceOf[Definition.Atom].name.name shouldEqual "Foo"
|
||||
ir.bindings(1).asInstanceOf[Definition.Atom].name.name shouldEqual "Bar"
|
||||
}
|
||||
|
||||
"have annotations on the type desugared to annotations on the defined" in {
|
||||
val ir =
|
||||
"""@Builtin_Type
|
||||
|type My_Type
|
||||
| Foo
|
||||
| type Bar
|
||||
|""".stripMargin.preprocessModule.desugar
|
||||
|
||||
exactly(1, ir.bindings) shouldBe a[Definition.Atom]
|
||||
ir.bindings.head
|
||||
.asInstanceOf[Definition.Atom]
|
||||
.unsafeGetMetadata(ModuleAnnotations, "")
|
||||
.annotations
|
||||
.head
|
||||
.name shouldEqual "@Builtin_Type"
|
||||
}
|
||||
|
||||
"have their methods desugared to methods on included atoms" in {
|
||||
ir.bindings(3) shouldBe an[Definition.Method.Binding]
|
||||
val justIsJust = ir.bindings(3).asInstanceOf[Definition.Method.Binding]
|
||||
|
@ -1,13 +1,13 @@
|
||||
package org.enso.compiler.test.pass.resolve
|
||||
|
||||
import org.enso.compiler.Passes
|
||||
import org.enso.compiler.context.{FreshNameSupply, ModuleContext}
|
||||
import org.enso.compiler.context.{FreshNameSupply, InlineContext, ModuleContext}
|
||||
import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.pass.resolve.Annotations
|
||||
import org.enso.compiler.pass.resolve.ExpressionAnnotations
|
||||
import org.enso.compiler.pass.{PassConfiguration, PassGroup, PassManager}
|
||||
import org.enso.compiler.test.CompilerTest
|
||||
|
||||
class AnnotationsTest extends CompilerTest {
|
||||
class ExpressionAnnotationsTest extends CompilerTest {
|
||||
|
||||
// === Test Setup ===========================================================
|
||||
|
||||
@ -16,10 +16,15 @@ class AnnotationsTest extends CompilerTest {
|
||||
freshNameSupply = Some(new FreshNameSupply)
|
||||
)
|
||||
|
||||
def mkInlineContext: InlineContext =
|
||||
buildInlineContext(
|
||||
freshNameSupply = Some(new FreshNameSupply)
|
||||
)
|
||||
|
||||
val passes = new Passes
|
||||
|
||||
val precursorPasses: PassGroup =
|
||||
passes.getPrecursors(Annotations).get
|
||||
passes.getPrecursors(ExpressionAnnotations).get
|
||||
|
||||
val passConfiguration: PassConfiguration = PassConfiguration()
|
||||
|
||||
@ -38,7 +43,13 @@ class AnnotationsTest extends CompilerTest {
|
||||
* @return [[ir]], with tail call analysis metadata attached
|
||||
*/
|
||||
def analyse(implicit context: ModuleContext): IR.Module = {
|
||||
Annotations.runModule(ir, context)
|
||||
ExpressionAnnotations.runModule(ir, context)
|
||||
}
|
||||
}
|
||||
|
||||
implicit class AnalyseExpression(ir: IR.Expression) {
|
||||
def analyse(implicit context: InlineContext): IR.Expression = {
|
||||
ExpressionAnnotations.runExpression(ir, context)
|
||||
}
|
||||
}
|
||||
|
||||
@ -52,39 +63,44 @@ class AnnotationsTest extends CompilerTest {
|
||||
|foo x =
|
||||
| @Tail_Call
|
||||
| @Unknown_Annotation foo bar baz
|
||||
| @Builtin_Method "myBuiltin"
|
||||
| foo @Tail_Call
|
||||
| foo (@Tail_Call bar baz)
|
||||
|""".stripMargin.preprocessModule.analyse
|
||||
|
||||
"resolve and mark annotations" in {
|
||||
val items = ir.bindings.head
|
||||
.asInstanceOf[IR.Module.Scope.Definition.Method.Explicit]
|
||||
.body
|
||||
.asInstanceOf[IR.Function.Lambda]
|
||||
.body
|
||||
.asInstanceOf[IR.Expression.Block]
|
||||
val items = ir.bindings.head
|
||||
.asInstanceOf[IR.Module.Scope.Definition.Method.Explicit]
|
||||
.body
|
||||
.asInstanceOf[IR.Function.Lambda]
|
||||
.body
|
||||
.asInstanceOf[IR.Expression.Block]
|
||||
|
||||
"create an error when discovering an unexpected annotation" in {
|
||||
items.expressions(0) shouldBe an[IR.Error.Resolution]
|
||||
items
|
||||
.expressions(0)
|
||||
.asInstanceOf[IR.Error.Resolution]
|
||||
.reason shouldEqual IR.Error.Resolution.UnexpectedTailCallAnnotation
|
||||
.reason shouldEqual IR.Error.Resolution.UnexpectedAnnotation
|
||||
|
||||
}
|
||||
|
||||
"create an error when discovering an unknown annotation" in {
|
||||
val unknown =
|
||||
items.expressions(1).asInstanceOf[IR.Application.Prefix].function
|
||||
unknown shouldBe an[IR.Error.Resolution]
|
||||
unknown
|
||||
.asInstanceOf[IR.Error.Resolution]
|
||||
.reason shouldEqual IR.Error.Resolution.UnknownAnnotation
|
||||
}
|
||||
|
||||
val misplaced = items
|
||||
.expressions(2)
|
||||
.asInstanceOf[IR.Application.Prefix]
|
||||
.arguments(0)
|
||||
.value
|
||||
misplaced shouldBe an[IR.Error.Resolution]
|
||||
misplaced
|
||||
.asInstanceOf[IR.Error.Resolution]
|
||||
.reason shouldEqual IR.Error.Resolution.UnexpectedTailCallAnnotation
|
||||
"associate the annotation with the annotated definition" in {
|
||||
val correctDef = items.expressions(2).asInstanceOf[IR.Literal.Text]
|
||||
correctDef.text shouldEqual "myBuiltin"
|
||||
correctDef
|
||||
.unsafeGetMetadata(ExpressionAnnotations, "")
|
||||
.annotations
|
||||
.head
|
||||
.name shouldEqual "@Builtin_Method"
|
||||
|
||||
val correct = items.returnValue
|
||||
.asInstanceOf[IR.Application.Prefix]
|
||||
@ -93,9 +109,24 @@ class AnnotationsTest extends CompilerTest {
|
||||
.asInstanceOf[IR.Application.Prefix]
|
||||
correct.function.asInstanceOf[IR.Name].name shouldEqual "bar"
|
||||
correct.arguments.length shouldEqual 1
|
||||
correct.getMetadata(Annotations) should contain(
|
||||
Annotations.TailCallAnnotated
|
||||
)
|
||||
correct
|
||||
.getMetadata(ExpressionAnnotations)
|
||||
.get
|
||||
.annotations
|
||||
.head
|
||||
.name shouldEqual "@Tail_Call"
|
||||
}
|
||||
|
||||
"create an error on a misplaced annotation" in {
|
||||
val misplaced = items
|
||||
.expressions(3)
|
||||
.asInstanceOf[IR.Application.Prefix]
|
||||
.arguments(0)
|
||||
.value
|
||||
misplaced shouldBe an[IR.Error.Resolution]
|
||||
misplaced
|
||||
.asInstanceOf[IR.Error.Resolution]
|
||||
.reason shouldEqual IR.Error.Resolution.UnexpectedAnnotation
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,191 @@
|
||||
package org.enso.compiler.test.pass.resolve
|
||||
|
||||
import org.enso.compiler.Passes
|
||||
import org.enso.compiler.context.ModuleContext
|
||||
import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.core.IR.Module.Scope.Definition
|
||||
import org.enso.compiler.pass.{PassConfiguration, PassGroup, PassManager}
|
||||
import org.enso.compiler.pass.resolve.ModuleAnnotations
|
||||
import org.enso.compiler.test.CompilerTest
|
||||
|
||||
class ModuleAnnotationsTest extends CompilerTest {
|
||||
|
||||
// === Test Setup ===========================================================
|
||||
|
||||
val passes = new Passes
|
||||
|
||||
val precursorPasses: PassGroup = passes.getPrecursors(ModuleAnnotations).get
|
||||
|
||||
val passConfiguration: PassConfiguration = PassConfiguration();
|
||||
|
||||
implicit val passManager: PassManager =
|
||||
new PassManager(List(precursorPasses), passConfiguration)
|
||||
|
||||
/** Adds an extension method for running module-level annotations resolution
|
||||
* on the input IR.
|
||||
*
|
||||
* @param ir the IR to resolve
|
||||
*/
|
||||
implicit class ResolveModule(ir: IR.Module) {
|
||||
|
||||
/** Runs annotation resolution on an [[IR.Module]].
|
||||
*
|
||||
* @param moduleContext the module context in which the resolution takes
|
||||
* place
|
||||
* @return [[ir]], with all module-level annotations resolved
|
||||
*/
|
||||
def resolve(implicit moduleContext: ModuleContext): IR.Module = {
|
||||
ModuleAnnotations.runModule(ir, moduleContext)
|
||||
}
|
||||
}
|
||||
|
||||
/** Makes a module context.
|
||||
*
|
||||
* @return a fresh module context
|
||||
*/
|
||||
def mkModuleContext: ModuleContext = {
|
||||
buildModuleContext()
|
||||
}
|
||||
|
||||
// === The Tests ============================================================
|
||||
|
||||
"Annotation desugaring at the top level" should {
|
||||
implicit val moduleContext: ModuleContext = mkModuleContext
|
||||
|
||||
"associate annotations with atom definitions" in {
|
||||
val ir =
|
||||
"""@My_Annotation_1
|
||||
|@My_Annotation_2
|
||||
|type My_Atom
|
||||
|""".stripMargin.preprocessModule.resolve
|
||||
|
||||
ir.bindings.length shouldEqual 1
|
||||
ir.bindings.head shouldBe a[Definition.Atom]
|
||||
val anns =
|
||||
ir.bindings.head.unsafeGetMetadata(ModuleAnnotations, "").annotations
|
||||
anns.length shouldEqual 2
|
||||
anns.head.asInstanceOf[IR.Name].name shouldEqual "@My_Annotation_1"
|
||||
anns(1).asInstanceOf[IR.Name].name shouldEqual "@My_Annotation_2"
|
||||
}
|
||||
|
||||
"associate annotations with complex type definitions" in {
|
||||
val ir =
|
||||
"""@My_Annotation_1
|
||||
|@My_Annotation_2
|
||||
|type Foo
|
||||
| type Bar
|
||||
|""".stripMargin.preprocessModule.resolve
|
||||
|
||||
ir.bindings.length shouldEqual 1
|
||||
ir.bindings.head shouldBe a[Definition.Type]
|
||||
val anns =
|
||||
ir.bindings.head.unsafeGetMetadata(ModuleAnnotations, "").annotations
|
||||
anns.length shouldEqual 2
|
||||
anns.head.asInstanceOf[IR.Name].name shouldEqual "@My_Annotation_1"
|
||||
anns(1).asInstanceOf[IR.Name].name shouldEqual "@My_Annotation_2"
|
||||
}
|
||||
|
||||
"associate annotations with method definitions" in {
|
||||
val ir =
|
||||
"""@My_Annotation_1
|
||||
|@My_Annotation_2
|
||||
|My_Type.add a b = this.frob(a + b)
|
||||
|""".stripMargin.preprocessModule.resolve
|
||||
|
||||
ir.bindings.length shouldEqual 1
|
||||
ir.bindings.head shouldBe a[Definition.Method.Binding]
|
||||
val anns =
|
||||
ir.bindings.head.unsafeGetMetadata(ModuleAnnotations, "").annotations
|
||||
anns.length shouldEqual 2
|
||||
anns.head.asInstanceOf[IR.Name].name shouldEqual "@My_Annotation_1"
|
||||
anns(1).asInstanceOf[IR.Name].name shouldEqual "@My_Annotation_2"
|
||||
}
|
||||
|
||||
"not associate annotations with comments" in {
|
||||
val ir =
|
||||
"""@My_Annotation
|
||||
|## My doc comment
|
||||
|type Foo
|
||||
|""".stripMargin.preprocessModule.resolve
|
||||
|
||||
ir.bindings.length shouldEqual 2
|
||||
ir.bindings(1) shouldBe a[Definition.Atom]
|
||||
val anns =
|
||||
ir.bindings(1).unsafeGetMetadata(ModuleAnnotations, "").annotations
|
||||
anns.length shouldEqual 1
|
||||
anns.head.asInstanceOf[IR.Name].name shouldEqual "@My_Annotation"
|
||||
}
|
||||
}
|
||||
|
||||
"Annotation desugaring in complex types" should {
|
||||
implicit val moduleContext: ModuleContext = mkModuleContext
|
||||
|
||||
"associate annotations with atom definitions" in {
|
||||
val ir =
|
||||
"""@My_Annotation
|
||||
|type Foo
|
||||
| @My_Annotation
|
||||
| type Bar
|
||||
|""".stripMargin.preprocessModule.resolve
|
||||
|
||||
ir.bindings.length shouldEqual 1
|
||||
ir.bindings.head shouldBe a[Definition.Type]
|
||||
val typ = ir.bindings.head.asInstanceOf[Definition.Type]
|
||||
typ.body.length shouldEqual 1
|
||||
typ.body.head shouldBe a[Definition.Atom]
|
||||
typ.body.head
|
||||
.unsafeGetMetadata(ModuleAnnotations, "")
|
||||
.annotations
|
||||
.head
|
||||
.asInstanceOf[IR.Name]
|
||||
.name shouldEqual "@My_Annotation"
|
||||
}
|
||||
|
||||
"associate annotations with method definitions" in {
|
||||
val ir =
|
||||
"""type Foo
|
||||
| type Foo
|
||||
|
|
||||
| @My_Annotation
|
||||
| my_method a = a
|
||||
|""".stripMargin.preprocessModule.resolve
|
||||
|
||||
ir.bindings.length shouldEqual 1
|
||||
ir.bindings.head shouldBe a[Definition.Type]
|
||||
val typ = ir.bindings.head.asInstanceOf[Definition.Type]
|
||||
typ.body.length shouldEqual 2
|
||||
typ.body(1) shouldBe an[IR.Function.Binding]
|
||||
typ
|
||||
.body(1)
|
||||
.unsafeGetMetadata(ModuleAnnotations, "")
|
||||
.annotations
|
||||
.head
|
||||
.asInstanceOf[IR.Name]
|
||||
.name shouldEqual "@My_Annotation"
|
||||
}
|
||||
|
||||
"not associate annotations with comments" in {
|
||||
val ir =
|
||||
"""
|
||||
|type Foo
|
||||
| @My_Annotation
|
||||
| ## Doc comment
|
||||
| type Foo
|
||||
|""".stripMargin.preprocessModule.resolve
|
||||
|
||||
ir.bindings.length shouldEqual 1
|
||||
ir.bindings.head shouldBe a[Definition.Type]
|
||||
val typ = ir.bindings.head.asInstanceOf[Definition.Type]
|
||||
typ.body.length shouldEqual 2
|
||||
typ.body.head shouldBe an[IR.Comment]
|
||||
typ.body(1) shouldBe a[Definition.Atom]
|
||||
typ
|
||||
.body(1)
|
||||
.unsafeGetMetadata(ModuleAnnotations, "")
|
||||
.annotations
|
||||
.head
|
||||
.asInstanceOf[IR.Name]
|
||||
.name shouldEqual "@My_Annotation"
|
||||
}
|
||||
}
|
||||
}
|
@ -4,7 +4,11 @@ import org.enso.compiler.Passes
|
||||
import org.enso.compiler.context.{FreshNameSupply, InlineContext, ModuleContext}
|
||||
import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.pass.{PassConfiguration, PassGroup, PassManager}
|
||||
import org.enso.compiler.pass.resolve.{DocumentationComments, TypeSignatures}
|
||||
import org.enso.compiler.pass.resolve.{
|
||||
DocumentationComments,
|
||||
ModuleAnnotations,
|
||||
TypeSignatures
|
||||
}
|
||||
import org.enso.compiler.test.CompilerTest
|
||||
|
||||
class TypeSignaturesTest extends CompilerTest {
|
||||
@ -86,7 +90,6 @@ class TypeSignaturesTest extends CompilerTest {
|
||||
ir.bindings.head.getMetadata(TypeSignatures) shouldBe defined
|
||||
}
|
||||
|
||||
// TODO [AA] This isn't consistent.
|
||||
"allow dotted paths in type signatures" in {
|
||||
val ir =
|
||||
"""
|
||||
@ -96,7 +99,6 @@ class TypeSignaturesTest extends CompilerTest {
|
||||
|
||||
ir.bindings.length shouldEqual 1
|
||||
ir.bindings.head.getMetadata(TypeSignatures) shouldBe defined
|
||||
// println(ir.bindings.head.getMetadata(TypeSignatures).get.signature.pretty)
|
||||
}
|
||||
|
||||
"raise an error if a signature is divorced from its definition" in {
|
||||
@ -132,6 +134,18 @@ class TypeSignaturesTest extends CompilerTest {
|
||||
ir.bindings.head.getMetadata(DocumentationComments) shouldBe defined
|
||||
}
|
||||
|
||||
"reattach annotations to method definitions" in {
|
||||
val ir =
|
||||
"""@Builtin_Type
|
||||
|bar : Number -> Number -> Number
|
||||
|bar a b = a + b
|
||||
|""".stripMargin.preprocessModule.resolve
|
||||
|
||||
ir.bindings.length shouldEqual 1
|
||||
ir.bindings.head.getMetadata(TypeSignatures) shouldBe defined
|
||||
ir.bindings.head.getMetadata(ModuleAnnotations) shouldBe defined
|
||||
}
|
||||
|
||||
"work inside type definition bodies" in {
|
||||
val ir =
|
||||
"""
|
||||
|
@ -1,13 +1,5 @@
|
||||
package org.enso.interpreter.test
|
||||
|
||||
import java.io.{
|
||||
ByteArrayOutputStream,
|
||||
PipedInputStream,
|
||||
PipedOutputStream,
|
||||
PrintStream
|
||||
}
|
||||
import java.util.UUID
|
||||
|
||||
import com.oracle.truffle.api.instrumentation.EventBinding
|
||||
import org.enso.interpreter.test.CodeIdsTestInstrument.IdEventListener
|
||||
import org.enso.interpreter.test.CodeLocationsTestInstrument.LocationsEventListener
|
||||
@ -27,6 +19,14 @@ import org.graalvm.polyglot.{Context, Value}
|
||||
import org.scalatest.Assertions
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
import org.scalatest.wordspec.AnyWordSpec
|
||||
|
||||
import java.io.{
|
||||
ByteArrayOutputStream,
|
||||
PipedInputStream,
|
||||
PipedOutputStream,
|
||||
PrintStream
|
||||
}
|
||||
import java.util.UUID
|
||||
case class LocationsInstrumenter(instrument: CodeLocationsTestInstrument) {
|
||||
var bindings: List[EventBinding[LocationsEventListener]] = List()
|
||||
|
||||
@ -235,7 +235,7 @@ trait InterpreterBehavior {
|
||||
|
||||
def specify(implicit interpreterContext: InterpreterContext): Unit
|
||||
|
||||
def contextModifiers: Context#Builder => Context#Builder = bldr => bldr
|
||||
def contextModifiers: Option[Context#Builder => Context#Builder] = None
|
||||
}
|
||||
|
||||
trait InterpreterTest
|
||||
@ -247,18 +247,35 @@ trait InterpreterTest
|
||||
|
||||
subject when {
|
||||
"Context is Cached" should {
|
||||
behave like specify(new InterpreterContext(contextModifiers))
|
||||
behave like specify(contextModifiers match {
|
||||
case Some(mods) => new InterpreterContext(mods)
|
||||
case None => GlobalContexts.cachedContext
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
subject when {
|
||||
"Context is Uncached" should {
|
||||
behave like specify(
|
||||
new InterpreterContext(
|
||||
contextModifiers
|
||||
.andThen(_.option(RuntimeOptions.DISABLE_INLINE_CACHES, "true"))
|
||||
)
|
||||
contextModifiers match {
|
||||
case Some(mods) =>
|
||||
new InterpreterContext(
|
||||
mods.andThen(
|
||||
_.option(RuntimeOptions.DISABLE_INLINE_CACHES, "true")
|
||||
)
|
||||
)
|
||||
case None => GlobalContexts.uncachedContext
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object GlobalContexts {
|
||||
val defaultMods: Context#Builder => Context#Builder = bldr => bldr
|
||||
val cachedContext =
|
||||
new InterpreterContext(defaultMods)
|
||||
val uncachedContext = new InterpreterContext(
|
||||
_.option(RuntimeOptions.DISABLE_INLINE_CACHES, "true")
|
||||
)
|
||||
}
|
||||
|
@ -9,8 +9,8 @@ class ReplTest extends InterpreterTest with BeforeAndAfter with EitherValues {
|
||||
|
||||
override def subject: String = "Repl"
|
||||
|
||||
override def contextModifiers: Context#Builder => Context#Builder =
|
||||
_.option(DebugServerInfo.ENABLE_OPTION, "true")
|
||||
override def contextModifiers: Option[Context#Builder => Context#Builder] =
|
||||
Some(_.option(DebugServerInfo.ENABLE_OPTION, "true"))
|
||||
|
||||
override def specify(implicit
|
||||
interpreterContext: InterpreterContext
|
||||
|
@ -7,7 +7,7 @@ class ImportsTest extends PackageTest {
|
||||
evalTestProject("TestSimpleImports") shouldEqual 20
|
||||
}
|
||||
|
||||
"Methods defined together with atom" should "be visible even if not imported" in {
|
||||
"Methods defined together with atoms" should "be visible even if not imported" in {
|
||||
evalTestProject("TestNonImportedOwnMethods") shouldEqual 10
|
||||
}
|
||||
|
||||
|
@ -11,8 +11,8 @@ import org.graalvm.polyglot.Context
|
||||
class OverloadsResolutionErrorTest extends InterpreterTest {
|
||||
override def subject: String = "Symbol Overloads"
|
||||
|
||||
override def contextModifiers: Context#Builder => Context#Builder =
|
||||
_.option(RuntimeOptions.STRICT_ERRORS, "true")
|
||||
override def contextModifiers: Option[Context#Builder => Context#Builder] =
|
||||
Some(_.option(RuntimeOptions.STRICT_ERRORS, "true"))
|
||||
|
||||
override def specify(implicit
|
||||
interpreterContext: InterpreterContext
|
||||
|
@ -11,8 +11,8 @@ import org.graalvm.polyglot.Context
|
||||
class StrictCompileDiagnosticsTest extends InterpreterTest {
|
||||
override def subject: String = "Compile Errors in Batch Mode"
|
||||
|
||||
override def contextModifiers: Context#Builder => Context#Builder =
|
||||
_.option(RuntimeOptions.STRICT_ERRORS, "true")
|
||||
override def contextModifiers: Option[Context#Builder => Context#Builder] =
|
||||
Some(_.option(RuntimeOptions.STRICT_ERRORS, "true"))
|
||||
|
||||
override def specify(implicit
|
||||
interpreterContext: InterpreterContext
|
||||
|
@ -191,7 +191,7 @@ object Application {
|
||||
prettyName: String,
|
||||
helpHeader: String,
|
||||
commands: NonEmptyList[Command[Unit => Int]]
|
||||
): Application[()] =
|
||||
): Application[Unit] =
|
||||
new Application(
|
||||
commandName,
|
||||
prettyName,
|
||||
|
@ -92,6 +92,7 @@ object Builtin {
|
||||
}
|
||||
case _ => internalError
|
||||
}
|
||||
case _ => internalError
|
||||
}
|
||||
}
|
||||
|
||||
@ -195,6 +196,7 @@ object Builtin {
|
||||
AST.App.Infix(l.wrapped, Opr("->"), r.wrapped)
|
||||
case _ => internalError
|
||||
}
|
||||
case _ => internalError
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,12 +25,6 @@ spec = describe "Pattern Matches" <|
|
||||
case Integer of
|
||||
Integer -> Nothing
|
||||
_ -> Test.fail "Expected the Integer constructor to match."
|
||||
case Big_Integer of
|
||||
Integer -> Nothing
|
||||
_ -> Test.fail "Expected the Big_Integer constructor to match."
|
||||
case Small_Integer of
|
||||
Integer -> Nothing
|
||||
_ -> Test.fail "Expected the Small_Integer constructor to match."
|
||||
it "should be able to match on the Decimal type" <|
|
||||
case 1.7 of
|
||||
Decimal -> Nothing
|
||||
@ -51,12 +45,6 @@ spec = describe "Pattern Matches" <|
|
||||
case Number of
|
||||
Number -> Nothing
|
||||
_ -> Test.fail "Expected the Number constructor to match."
|
||||
case Small_Integer of
|
||||
Number -> Nothing
|
||||
_ -> Test.fail "Expected the Small_Integer constructor to match."
|
||||
case Big_Integer of
|
||||
Number -> Nothing
|
||||
_ -> Test.fail "Expected the Big_Integer constructor to match."
|
||||
case Integer of
|
||||
Number -> Nothing
|
||||
_ -> Test.fail "Expected the Integer constructor to match."
|
||||
|
Loading…
Reference in New Issue
Block a user