Autoscoped constructors (#9190)

Fixes #8645 by recognizing `~` prefix to constructor names.
This commit is contained in:
Jaroslav Tulach 2024-03-04 12:41:02 +01:00 committed by GitHub
parent 3be5e585f2
commit 5676618bad
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
31 changed files with 762 additions and 331 deletions

View File

@ -1059,6 +1059,7 @@
- [Add run_google_report method][8907]
- [Execute and debug individual Enso files in VSCode extension][8923]
- [Check type of `self` when calling a method using the static syntax][8867]
- [Autoscoped constructors][9190]
[3227]: https://github.com/enso-org/enso/pull/3227
[3248]: https://github.com/enso-org/enso/pull/3248
@ -1217,6 +1218,7 @@
[8907]: https://github.com/enso-org/enso/pull/8907
[8923]: https://github.com/enso-org/enso/pull/8923
[8867]: https://github.com/enso-org/enso/pull/8867
[9190]: https://github.com/enso-org/enso/pull/9190
# Enso 2.0.0-alpha.18 (2021-10-12)

View File

@ -79,7 +79,15 @@ type Type_Error
_ -> ". Try to apply " + (missing_args.join ", ") + " arguments"
_ -> tpe.to_text
"Type error: expected "+self.comment+" to be "+self.expected.to_display_text+", but got "+(if type_of_actual.is_a Text then type_of_actual else "<ERR>")+"."
got = if type_of_actual.is_a Text then type_of_actual else "<ERR>"
exp = self.expected.to_display_text
msg = self.comment
. replace "{exp}" exp
. replace "{got}" got
"Type error: "+msg+"."
to_text : Text
to_text self = self.to_display_text
@Builtin_Type
type Compile_Error

View File

@ -14,20 +14,11 @@ priority, but the dependencies between tasks are described.
## Technology Choices
With the advent of Java 17 and its ergonomic improvements (read:
pattern-matching), it makes little sense to retain the usage of Scala throughout
the compiler. The language was originally introduced due to the capabilities of
its type system in comparison to Java's, but very little of this functionality
has been used in the end.
We recommend moving everything to Java as part of this work, as you will end up
with better tooling support. Scala has been a problem child.
Enso originally started working with Java 8, and was transitioned (painfully,
due to the JPMS) to Java 11. Java 8 was EOL'd by the graal team after a couple
of years. It seems likely that Java 11 will suffer a similar fate, though the
transition from 11 to 17 will be far less painful as it doesn't introduce any
breaking language-level changes.
Enso interpreter is written in a mixture of Scala and Java. Scala was originally
used due to the capabilities of its type system in comparison to Java's. Modern
Java (as provided by JDK 21 or [Frgaal compiler](http://frgaal.org)) meets most
of the needs too. The ultimate goal is to write everything in Java and also keep
up with most recent long term supported JDK/GraalVM releases.
## Static Analysis
@ -49,7 +40,8 @@ Currently, the IR is:
- Very verbose and difficult to add a new node to. Adding a new node requires
adding ~100 lines of code that could likely be automated away. Lots of
boilerplate.
- Of unknown performance.
- Of poor performance as witnessed by
[static compiler benchmarks](https://github.com/enso-org/enso/pull/9158)
- Partially mutable, making it confusing as to which things are shared.
A new IR for Enso would have to:
@ -252,25 +244,6 @@ To rectify this situation, we recommend implementing a system we have termed
With this done, it may still be necessary to create a Java DSL for implementing
built-in methods and types, but that is unclear at this point.
### Static Methods on Types
Currently, Enso allows calling methods on _modules_, _constructors_, and
_instances_. This does not conform to the language specification because it
allows constructors and instances to be treated the same at runtime. This leads
to odd results (see the ticket below).
The end result should be compliant with the design described
[here](https://github.com/enso-org/enso/issues/1851), and needs to be taken into
account when defining builtins.
### Better Safepointing
Enso currently uses a hand-rolled safepointing system for interrupting threads
and handling resource finalisation. With 21.1, Truffle landed its own system for
doing this. Enso should be updated to use
[the new system](https://github.com/oracle/graal/blob/master/truffle/docs/Safepoints.md),
instead, as it will provide better performance and more robust operation.
## Runtime Performance
While Enso is performant when it gets JITted by GraalVM, the performance when
@ -290,29 +263,6 @@ This can be greatly improved.
be improved.
- Many of the above-listed static optimisations will greatly help here.
### Unboxed Atoms
Currently every atom in Enso is stored boxed. In limited circumstances it may be
possible to unbox these and hence remove the indirection cost when accessing
their data.
- Read the details of Truffle's
[`DynamicObject`](https://www.graalvm.org/truffle/javadoc/com/oracle/truffle/api/object/DynamicObject.html),
and the sources.
- Use this system to inform the design for a system that reduces the overhead of
dynamic field names and arities when accessing data on Atoms.
### Unboxed Vectors
Enso currently doesn't have support for unboxed arrays (and hence vectors). This
means that it incurs a significant performance cost when working with pure
numerical arrays. This can be improved.
- Read the truffle documentation on
[truffle libraries](https://github.com/oracle/graal/blob/master/truffle/docs/TruffleLibraries.md).
- Based on this, define a system that seamlessly specializes and deoptimises
between boxed and unboxed arrays as necessary.
## IDE
As Enso's primary mode of use is in the IDE, there are a number of important
@ -362,7 +312,3 @@ preprocessors.
- Implement caching support for the visualization expression processing.
- This cache should, much like the IDE's introspection cache, track and save the
values of all top-level bindings in the visualization preprocessor.
## Parser
Parser

View File

@ -87,7 +87,7 @@ binds the function name. This means that:
user-defined type for the function.
```ruby
sum : (a: Monoid) -> a -> a
sum : (a:Monoid) -> a -> a
sum : x -> y -> x + y
sum x y = x + y
```
@ -113,11 +113,11 @@ Methods can be defined in Enso in two ways:
```ruby
type Maybe a
Nothing
type Just (value : a)
Just (value : a)
isJust = case this of
Nothing -> False
Just _ -> True
is_just self = case self of
Maybe.Nothing -> False
Maybe.Just _ -> True
```
2. **As an Extension Method:** A function defined _explicitly_ on an atom counts
@ -125,7 +125,7 @@ type Maybe a
to all the atoms within that typeset.
```ruby
Number.floor = case this of
Number.floor self = case self of
Integer -> ...
...
```

View File

@ -276,32 +276,48 @@ context-dependent manner that is discussed properly in the
[type system design document](../types/README.md), but is summarised briefly
below.
- **Name and Fields:** When you provide the keyword with only a name and some
field names, this creates an atom.
```ruby
type Just value
```
- **Body with Atom Definitions:** If you provide a body with atom definitions,
this defines a smart constructor that defines the atoms and related functions
by returning a typeset.
```ruby
type Maybe a
type Maybe
Nothing
type Just (value : a)
Just (value : Integer)
isJust = case this of
Nothing -> False
Just _ -> True
is_just self = case self of
Maybe.Nothing -> False
Maybe.Just _ -> True
nothing = not isJust
nothing self = self.is_just.not
```
Please note that the `type Foo (a : t)` is syntax only allowable inside a type
definition. It defines an atom `Foo`, but constrains the type variable of the
atom _in this usage_.
To reference atoms use type name followed by the name of the atom. E.g.
`Maybe.Nothing` or `Maybe.Just 2`. Atom constructors act like functions and
fully support currying - e.g. one can create `fn = Maybe.Just` and later apply
two to it (`fn 2`) to obtain new atom.
- **Autoscoped Constructors:** Referencing constructors via their type name may
lead to long and boilerplate code. To simplify referencing constructors when
the _context is known_ a special `~` syntax is supported. Should there be a
method `describe`:
```ruby
describe (m : Maybe) -> Text = if m.is_just then m.value.to_text else "Empty"
```
one may invoke it as `describe (Maybe.Just 5)` - e.g. the regular way. Or one
may use _autoscoped constructors_ and call
```ruby
describe (~Just 5)
```
the argument `(~Just 5)` is _recorded but not executed_ until it is send to
the `describe` method. The argument of the `describe` method is known to be of
type `Maybe` and have `Just` constructor. The _scope_ is now known and the so
far deferred `~` value gets evaluated. `Maybe.Just 5` atom is constructed and
execution of `describe` method continues with such atom.
- **Body Without Atom Definitions:** If you provide a body and do not define any
atoms within it, this creates an interface that asserts no atoms as part of

View File

@ -1,15 +1,18 @@
package org.enso.interpreter.bench.benchmarks.semantic;
import java.nio.file.Paths;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.logging.Level;
import org.enso.polyglot.RuntimeOptions;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Engine;
import org.graalvm.polyglot.Value;
import org.graalvm.polyglot.io.IOAccess;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.infra.BenchmarkParams;
import org.openjdk.jmh.infra.Blackhole;
@ -33,16 +36,7 @@ public class ArrayProxyBenchmarks {
@Setup
public void initializeBenchmark(BenchmarkParams params) throws Exception {
Engine eng =
Engine.newBuilder()
.allowExperimentalOptions(true)
.option(RuntimeOptions.LOG_LEVEL, Level.WARNING.getName())
.logHandler(System.err)
.option(
"enso.languageHomeOverride",
Paths.get("../../distribution/component").toFile().getAbsolutePath())
.build();
var ctx = Context.newBuilder().engine(eng).allowIO(IOAccess.ALL).allowAllAccess(true).build();
var ctx = SrcUtil.newContextBuilder().build();
var code =
"""
import Standard.Base.Data.Vector.Vector

View File

@ -1,13 +1,9 @@
package org.enso.interpreter.bench.benchmarks.semantic;
import java.nio.file.Paths;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import org.enso.interpreter.bench.Utils;
import org.enso.polyglot.RuntimeOptions;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value;
import org.graalvm.polyglot.io.IOAccess;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
@ -59,9 +55,28 @@ public class AtomBenchmarks {
import Standard.Base.Data.Numbers
main = length ->
generator = acc -> i -> if i == 0 then acc else @Tail_Call generator (List.Cons i acc) (i - 1)
qualified_generator = acc -> i -> if i == 0 then acc else
@Tail_Call qualified_generator (List.Cons i acc) (i - 1)
res = generator List.Nil length
res = qualified_generator List.Nil length
res
""";
private static final String GENERATE_LIST_AUTOSCOPING_CODE =
"""
import Standard.Base.Data.List.List
import Standard.Base.Data.Numbers
import Standard.Base.Data.Numbers.Integer
main = length ->
autoscoped_generator x i:Integer =
acc = x:List
if i == 0 then acc else
c = ~Cons i acc
i1 = i - 1
@Tail_Call autoscoped_generator c i1
res = autoscoped_generator ~Nil length
res
""";
private static final String REVERSE_LIST_CODE =
@ -174,6 +189,7 @@ public class AtomBenchmarks {
private Value millionElementsList;
private Value generateList;
private Value generateListQualified;
private Value generateListAutoscoping;
private Value reverseList;
private Value reverseListMethods;
private Value sumList;
@ -184,31 +200,54 @@ public class AtomBenchmarks {
private Value mapReverseListCurry;
@Setup
public void initializeBenchmarks(BenchmarkParams params) {
this.context =
Context.newBuilder()
.allowExperimentalOptions(true)
.option(RuntimeOptions.LOG_LEVEL, Level.WARNING.getName())
.logHandler(System.err)
.allowIO(IOAccess.ALL)
.allowAllAccess(true)
.option(
RuntimeOptions.LANGUAGE_HOME_OVERRIDE,
Paths.get("../../distribution/component").toFile().getAbsolutePath())
.build();
public void initializeBenchmarks(BenchmarkParams params) throws IOException {
this.context = SrcUtil.newContextBuilder().build();
var millionElemListMethod = Utils.getMainMethod(context, MILLION_ELEMENT_LIST);
var millionElemListMethod = mainMethod(context, "millionElementList", MILLION_ELEMENT_LIST);
this.millionElementsList = millionElemListMethod.execute();
this.generateList = Utils.getMainMethod(context, GENERATE_LIST_CODE);
this.generateListQualified = Utils.getMainMethod(context, GENERATE_LIST_QUALIFIED_CODE);
this.reverseList = Utils.getMainMethod(context, REVERSE_LIST_CODE);
this.reverseListMethods = Utils.getMainMethod(context, REVERSE_LIST_METHODS_CODE);
this.sumList = Utils.getMainMethod(context, SUM_LIST_CODE);
this.sumListLeftFold = Utils.getMainMethod(context, SUM_LIST_LEFT_FOLD_CODE);
this.sumListFallback = Utils.getMainMethod(context, SUM_LIST_FALLBACK_CODE);
this.sumListMethods = Utils.getMainMethod(context, SUM_LIST_METHODS_CODE);
this.mapReverseList = Utils.getMainMethod(context, MAP_REVERSE_LIST_CODE);
this.mapReverseListCurry = Utils.getMainMethod(context, MAP_REVERSE_LIST_CURRY_CODE);
var lastDot = params.getBenchmark().lastIndexOf('.');
var name = params.getBenchmark().substring(lastDot + 1);
switch (name) {
case "benchGenerateList" -> {
this.generateList = mainMethod(context, name, GENERATE_LIST_CODE);
}
case "benchGenerateListQualified" -> {
this.generateListQualified = mainMethod(context, name, GENERATE_LIST_QUALIFIED_CODE);
}
case "benchGenerateListAutoscoping" -> {
this.generateListAutoscoping = mainMethod(context, name, GENERATE_LIST_AUTOSCOPING_CODE);
}
case "benchReverseList" -> {
this.reverseList = mainMethod(context, name, REVERSE_LIST_CODE);
}
case "benchReverseListMethods" -> {
this.reverseListMethods = mainMethod(context, name, REVERSE_LIST_METHODS_CODE);
}
case "benchSumList" -> {
this.sumList = mainMethod(context, name, SUM_LIST_CODE);
}
case "benchSumListLeftFold" -> {
this.sumListLeftFold = mainMethod(context, name, SUM_LIST_LEFT_FOLD_CODE);
}
case "benchSumListFallback" -> {
this.sumListFallback = mainMethod(context, name, SUM_LIST_FALLBACK_CODE);
}
case "benchSumListMethods" -> {
this.sumListMethods = mainMethod(context, name, SUM_LIST_METHODS_CODE);
}
case "benchMapReverseList" -> {
this.mapReverseList = mainMethod(context, name, MAP_REVERSE_LIST_CODE);
}
case "benchMapReverseListCurry" -> {
this.mapReverseListCurry = mainMethod(context, name, MAP_REVERSE_LIST_CURRY_CODE);
}
default -> throw new IllegalArgumentException(name);
}
}
private static Value mainMethod(Context context, String name, String code) throws IOException {
return SrcUtil.getMainMethod(context, name, code);
}
@Benchmark
@ -223,6 +262,12 @@ public class AtomBenchmarks {
bh.consume(res);
}
@Benchmark
public void benchGenerateListAutoscoping(Blackhole bh) {
var res = generateListAutoscoping.execute(MILLION);
bh.consume(res);
}
@Benchmark
public void benchReverseList(Blackhole bh) {
var reversedList = reverseList.execute(millionElementsList);
@ -245,7 +290,7 @@ public class AtomBenchmarks {
}
@Benchmark
public void sumListLeftFold(Blackhole bh) {
public void benchSumListLeftFold(Blackhole bh) {
var res = sumListLeftFold.execute(millionElementsList);
if (!res.fitsInLong()) {
throw new AssertionError("Should return a number");
@ -278,7 +323,7 @@ public class AtomBenchmarks {
}
@Benchmark
public void benchMapReverseCurryList(Blackhole bh) {
public void benchMapReverseListCurry(Blackhole bh) {
var res = mapReverseListCurry.execute(millionElementsList);
bh.consume(res);
}

View File

@ -1,13 +1,9 @@
package org.enso.interpreter.bench.benchmarks.semantic;
import java.nio.file.Paths;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import org.enso.interpreter.bench.Utils;
import org.enso.polyglot.RuntimeOptions;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value;
import org.graalvm.polyglot.io.IOAccess;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
@ -81,17 +77,7 @@ main = sumTo ->
@Setup
public void initializeBenchmarks(BenchmarkParams params) {
this.context =
Context.newBuilder()
.allowExperimentalOptions(true)
.option(RuntimeOptions.LOG_LEVEL, Level.WARNING.getName())
.logHandler(System.err)
.allowIO(IOAccess.ALL)
.allowAllAccess(true)
.option(
RuntimeOptions.LANGUAGE_HOME_OVERRIDE,
Paths.get("../../distribution/component").toFile().getAbsolutePath())
.build();
this.context = SrcUtil.newContextBuilder().build();
this.sumTCOfromCall = Utils.getMainMethod(context, SUM_TCO_FROM_CALL_CODE);
this.sumTCOmethodCall = Utils.getMainMethod(context, SUM_TCO_METHOD_CALL_CODE);

View File

@ -1,6 +1,5 @@
package org.enso.interpreter.bench.benchmarks.semantic;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@ -8,12 +7,8 @@ import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import org.enso.polyglot.MethodNames.Module;
import org.enso.polyglot.RuntimeOptions;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value;
import org.graalvm.polyglot.io.IOAccess;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
@ -58,17 +53,7 @@ public class EqualsBenchmarks {
public void initializeBenchmark(BenchmarkParams params) throws Exception {
var random = new Random(42);
var ctx =
Context.newBuilder()
.allowExperimentalOptions(true)
.option(RuntimeOptions.LOG_LEVEL, Level.WARNING.getName())
.logHandler(System.err)
.allowIO(IOAccess.ALL)
.allowAllAccess(true)
.option(
"enso.languageHomeOverride",
Paths.get("../../distribution/component").toFile().getAbsolutePath())
.build();
var ctx = SrcUtil.newContextBuilder().build();
var benchmarkName = SrcUtil.findName(params);
var codeBuilder =

View File

@ -3,17 +3,13 @@ package org.enso.interpreter.bench.benchmarks.semantic;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import org.enso.polyglot.MethodNames.Module;
import org.enso.polyglot.RuntimeOptions;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value;
import org.graalvm.polyglot.io.IOAccess;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
@ -47,21 +43,7 @@ public class IfVsCaseBenchmarks {
@Setup
public void initializeBench(BenchmarkParams params) throws IOException {
OutputStream out = new ByteArrayOutputStream();
ctx =
Context.newBuilder("enso")
.allowAllAccess(true)
.option(RuntimeOptions.LOG_LEVEL, Level.WARNING.getName())
.logHandler(System.err)
.out(out)
.err(out)
.allowIO(IOAccess.ALL)
.allowExperimentalOptions(true)
.option(
"enso.languageHomeOverride",
Paths.get("../../distribution/component").toFile().getAbsolutePath())
.option("engine.MultiTier", "true")
.option("engine.BackgroundCompilation", "true")
.build();
ctx = SrcUtil.newContextBuilder().out(out).err(out).build();
var code =
"""

View File

@ -1,13 +1,8 @@
package org.enso.interpreter.bench.benchmarks.semantic;
import java.nio.file.Paths;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.logging.Level;
import org.enso.polyglot.RuntimeOptions;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value;
import org.graalvm.polyglot.io.IOAccess;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
@ -38,17 +33,7 @@ public class ListBenchmarks {
@Setup
public void initializeBenchmark(BenchmarkParams params) throws Exception {
var ctx =
Context.newBuilder()
.allowExperimentalOptions(true)
.allowIO(IOAccess.ALL)
.allowAllAccess(true)
.option(RuntimeOptions.LOG_LEVEL, Level.WARNING.getName())
.logHandler(System.err)
.option(
"enso.languageHomeOverride",
Paths.get("../../distribution/component").toFile().getAbsolutePath())
.build();
var ctx = SrcUtil.newContextBuilder().build();
var benchmarkName = SrcUtil.findName(params);
var code =

View File

@ -1,13 +1,9 @@
package org.enso.interpreter.bench.benchmarks.semantic;
import java.nio.file.Paths;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import org.enso.interpreter.bench.Utils;
import org.enso.polyglot.RuntimeOptions;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value;
import org.graalvm.polyglot.io.IOAccess;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
@ -57,18 +53,7 @@ main = sumTo ->
@Setup
public void initializeBenchmarks(BenchmarkParams params) {
this.context =
Context.newBuilder()
.allowExperimentalOptions(true)
.option(RuntimeOptions.LOG_LEVEL, Level.WARNING.getName())
.logHandler(System.err)
.allowIO(IOAccess.ALL)
.allowAllAccess(true)
.option(
RuntimeOptions.LANGUAGE_HOME_OVERRIDE,
Paths.get("../../distribution/component").toFile().getAbsolutePath())
.build();
this.context = SrcUtil.newContextBuilder().build();
this.sumTCOWithNamedArguments = Utils.getMainMethod(context, SUM_TCO_WITH_NAMED_ARGUMENTS_CODE);
this.sumTCOWithDefaultedArguments =
Utils.getMainMethod(context, SUM_TCO_WITH_DEFAULTED_ARGUMENTS_CODE);

View File

@ -1,14 +1,10 @@
package org.enso.interpreter.bench.benchmarks.semantic;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.logging.Level;
import org.enso.polyglot.RuntimeOptions;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value;
import org.graalvm.polyglot.io.IOAccess;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
@ -36,18 +32,7 @@ public class NestedPatternCompilationBenchmarks {
@Setup
public void initializeBenchmark(BenchmarkParams params) throws Exception {
ctx =
Context.newBuilder()
.allowExperimentalOptions(true)
.allowIO(IOAccess.ALL)
.allowAllAccess(true)
.option(RuntimeOptions.LOG_LEVEL, Level.WARNING.getName())
.logHandler(System.err)
.option(
"enso.languageHomeOverride",
Paths.get("../../distribution/component").toFile().getAbsolutePath())
.build();
ctx = SrcUtil.newContextBuilder().build();
benchmarkName = SrcUtil.findName(params);
code =
"""

View File

@ -1,14 +1,19 @@
package org.enso.interpreter.bench.benchmarks.semantic;
import java.nio.file.Paths;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import org.enso.interpreter.bench.Utils;
import org.enso.polyglot.RuntimeOptions;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value;
import org.graalvm.polyglot.io.IOAccess;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.infra.BenchmarkParams;
import org.openjdk.jmh.infra.Blackhole;
@ -114,17 +119,7 @@ main = n ->
@Setup
public void initializeBenchmarks(BenchmarkParams params) {
this.context =
Context.newBuilder()
.allowExperimentalOptions(true)
.option(RuntimeOptions.LOG_LEVEL, Level.WARNING.getName())
.logHandler(System.err)
.allowIO(IOAccess.ALL)
.allowAllAccess(true)
.option(
RuntimeOptions.LANGUAGE_HOME_OVERRIDE,
Paths.get("../../distribution/component").toFile().getAbsolutePath())
.build();
this.context = SrcUtil.newContextBuilder().build();
this.sumTCO = Utils.getMainMethod(context, SUM_TCO_CODE);
this.sumTCOWithEval = Utils.getMainMethod(context, SUM_TCO_WITH_EVAL_CODE);

View File

@ -3,8 +3,15 @@ package org.enso.interpreter.bench.benchmarks.semantic;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.Objects;
import java.util.logging.Level;
import org.enso.polyglot.MethodNames.Module;
import org.enso.polyglot.RuntimeOptions;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Source;
import org.graalvm.polyglot.Value;
import org.graalvm.polyglot.io.IOAccess;
import org.openjdk.jmh.infra.BenchmarkParams;
final class SrcUtil {
@ -30,4 +37,36 @@ final class SrcUtil {
Objects.requireNonNull(url, "Searching for " + resource);
return Source.newBuilder("enso", url).name(resource).build();
}
static Value getMainMethod(Context context, String benchmarkName, String code)
throws IOException {
var src = source(benchmarkName, code);
var module = context.eval(src);
var moduleType = module.invokeMember(Module.GET_ASSOCIATED_TYPE);
var main = module.invokeMember(Module.GET_METHOD, moduleType, "main");
if (!main.canExecute()) {
throw new AssertionError("Main method should be executable");
}
return main;
}
/**
* Typical builder suitable for benchmarking.
*
* @return preconfigured builder to use as a base for benchmarking
*/
static Context.Builder newContextBuilder() {
return Context.newBuilder()
.allowExperimentalOptions(true)
.option(RuntimeOptions.LOG_LEVEL, Level.WARNING.getName())
.logHandler(System.err)
.allowIO(IOAccess.ALL)
.allowAllAccess(true)
.option("engine.MultiTier", "false")
.option("engine.BackgroundCompilation", "false")
.option("engine.CompilationFailureAction", "Print")
.option(
RuntimeOptions.LANGUAGE_HOME_OVERRIDE,
Paths.get("../../distribution/component").toFile().getAbsolutePath());
}
}

View File

@ -1,13 +1,8 @@
package org.enso.interpreter.bench.benchmarks.semantic;
import java.nio.file.Paths;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.logging.Level;
import org.enso.polyglot.RuntimeOptions;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value;
import org.graalvm.polyglot.io.IOAccess;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
@ -34,18 +29,7 @@ public class StringBenchmarks {
@Setup
public void initializeBenchmark(BenchmarkParams params) throws Exception {
var ctx =
Context.newBuilder()
.allowExperimentalOptions(true)
.allowIO(IOAccess.ALL)
.allowAllAccess(true)
.option(RuntimeOptions.LOG_LEVEL, Level.WARNING.getName())
.logHandler(System.err)
.option(
"enso.languageHomeOverride",
Paths.get("../../distribution/component").toFile().getAbsolutePath())
.build();
var ctx = SrcUtil.newContextBuilder().build();
var code =
"""
from Standard.Base import all

View File

@ -1,15 +1,19 @@
package org.enso.interpreter.bench.benchmarks.semantic;
import java.nio.file.Paths;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.logging.Level;
import org.enso.polyglot.MethodNames.Module;
import org.enso.polyglot.RuntimeOptions;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value;
import org.graalvm.polyglot.io.IOAccess;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.infra.BenchmarkParams;
import org.openjdk.jmh.infra.Blackhole;
@ -26,17 +30,7 @@ public class TypePatternBenchmarks {
@Setup
public void initializeBenchmark(BenchmarkParams params) throws Exception {
var ctx =
Context.newBuilder()
.allowExperimentalOptions(true)
.allowIO(IOAccess.ALL)
.allowAllAccess(true)
.option(RuntimeOptions.LOG_LEVEL, Level.WARNING.getName())
.logHandler(System.err)
.option(
"enso.languageHomeOverride",
Paths.get("../../distribution/component").toFile().getAbsolutePath())
.build();
var ctx = SrcUtil.newContextBuilder().build();
var code =
"""
from Standard.Base import Integer, Vector, Any, Float

View File

@ -1,14 +1,9 @@
package org.enso.interpreter.bench.benchmarks.semantic;
import java.nio.file.Paths;
import java.util.AbstractList;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.logging.Level;
import org.enso.polyglot.RuntimeOptions;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value;
import org.graalvm.polyglot.io.IOAccess;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
@ -35,18 +30,7 @@ public class VectorBenchmarks {
@Setup
public void initializeBenchmark(BenchmarkParams params) throws Exception {
var ctx =
Context.newBuilder()
.allowExperimentalOptions(true)
.allowIO(IOAccess.ALL)
.allowAllAccess(true)
.option(RuntimeOptions.LOG_LEVEL, Level.WARNING.getName())
.logHandler(System.err)
.option(
"enso.languageHomeOverride",
Paths.get("../../distribution/component").toFile().getAbsolutePath())
.build();
var ctx = SrcUtil.newContextBuilder().build();
var benchmarkName = SrcUtil.findName(params);
var code =
"""

View File

@ -1,18 +1,14 @@
package org.enso.interpreter.bench.benchmarks.semantic;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import org.enso.polyglot.MethodNames;
import org.enso.polyglot.RuntimeOptions;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value;
import org.graalvm.polyglot.io.IOAccess;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
@ -71,17 +67,7 @@ public class WarningBenchmarks {
@Setup
public void initializeBench(BenchmarkParams params) throws IOException {
this.ctx =
Context.newBuilder()
.allowExperimentalOptions(true)
.option(RuntimeOptions.LOG_LEVEL, Level.WARNING.getName())
.logHandler(System.err)
.allowIO(IOAccess.ALL)
.allowAllAccess(true)
.option(
RuntimeOptions.LANGUAGE_HOME_OVERRIDE,
Paths.get("../../distribution/component").toFile().getAbsolutePath())
.build();
this.ctx = SrcUtil.newContextBuilder().build();
var random = new Random(42);
benchmarkName = SrcUtil.findName(params);

View File

@ -0,0 +1,226 @@
package org.enso.interpreter.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.ByteArrayOutputStream;
import org.enso.polyglot.MethodNames;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.PolyglotException;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
public class AutoscopedConstructorTest extends TestBase {
private static final ByteArrayOutputStream out = new ByteArrayOutputStream();
private static Context ctx;
public AutoscopedConstructorTest() {}
@BeforeClass
public static void prepareCtx() {
ctx = createDefaultContext(out);
}
@Before
public void resetOut() {
out.reset();
}
@AfterClass
public static void disposeCtx() {
ctx.close();
}
@Test
public void lazyConstructorWithNoArgument() {
try {
var create =
ctx.eval(
"enso",
"""
type N
False
materialize v:N = v.to_text
create n = N.materialize (~False)
""")
.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "create");
assertTrue("Can evaluate", create.canExecute());
assertEquals("False", create.execute(42).asString());
} catch (PolyglotException e) {
fail(e.getMessage() + " for \n" + out.toString());
}
}
@Test
public void lazyConstructorWithSingleArg() {
try {
var create =
ctx.eval(
"enso",
"""
type M
Construct value
materialize v:M = v.value
create n = M.materialize (~Construct n)
""")
.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "create");
assertTrue("Can evaluate", create.canExecute());
assertEquals("42", create.execute(42).toString());
} catch (PolyglotException e) {
fail(e.getMessage() + " for \n" + out.toString());
}
}
@Test
public void lazyConstructorWithTwoArgs() {
try {
var create =
ctx.eval(
"enso",
"""
type M
Construct v1 v2
materialize v:M = [v.v1, v.v2]
create a b = M.materialize (~Construct a b)
""")
.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "create");
assertTrue("Can evaluate", create.canExecute());
assertEquals("[6, 7]", create.execute(6, 7).toString());
} catch (PolyglotException e) {
fail(e.getMessage() + " for \n" + out.toString());
}
}
@Test
public void lazyConstructorWithTwoArgsCurried() {
try {
var create =
ctx.eval(
"enso",
"""
type M
Construct v1 v2
materialize v:M = [v.v1, v.v2]
create a b =
v0 = ~Construct
v1 = v0 a
v2 = v1 b
M.materialize v2
""")
.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "create");
assertTrue("Can evaluate", create.canExecute());
assertEquals("[7, 6]", create.execute(7, 6).toString());
} catch (PolyglotException e) {
fail(e.getMessage() + " for \n" + out.toString());
}
}
@Test
public void lazyConstructorWithTwoArgsNamed() {
try {
var create =
ctx.eval(
"enso",
"""
type M
Construct v1 v2
materialize v:M = [v.v1, v.v2]
create a b =
v0 = ~Construct v2=a v1=b
M.materialize v0
""")
.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "create");
assertTrue("Can evaluate", create.canExecute());
assertEquals("[7, 6]", create.execute(6, 7).toString());
} catch (PolyglotException e) {
fail(e.getMessage() + " for \n" + out.toString());
}
}
@Test
public void lazyConstructorWithNamedDefaultedArguments() {
try {
var module =
ctx.eval(
"enso",
"""
type M
Construct v1=1 v2=2 v3=3 v4=4
materialize v:M = [v.v1, v.v2, v.v3, v.v4]
c0 _ = M.materialize (~Construct)
c1 a = M.materialize (~Construct a)
c12 a b = M.materialize (~Construct a b)
c123 a b c = M.materialize (~Construct a b c)
c1234 a b c d = M.materialize (~Construct a b c d)
c14 a d = M.materialize (~Construct a v4=d)
c13 a c = M.materialize (~Construct a v3=c)
c41 a d = M.materialize ((~Construct v4=d) a)
c31 a c = M.materialize ((~Construct v3=c) a)
""");
var c0 = module.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "c0");
var c1 = module.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "c1");
var c12 = module.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "c12");
var c123 = module.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "c123");
var c1234 = module.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "c1234");
var c14 = module.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "c14");
var c13 = module.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "c13");
var c41 = module.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "c41");
var c31 = module.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "c31");
assertEquals("[1, 2, 3, 4]", c0.execute("ignored").toString());
assertEquals("[9, 2, 3, 4]", c1.execute(9).toString());
assertEquals("[9, 7, 3, 4]", c12.execute(9, 7).toString());
assertEquals("[9, 7, 5, 4]", c123.execute(9, 7, 5).toString());
assertEquals("[9, 7, 5, 3]", c1234.execute(9, 7, 5, 3).toString());
assertEquals("[8, 2, 3, 7]", c14.execute(8, 7).toString());
assertEquals("[8, 2, 7, 4]", c13.execute(8, 7).toString());
assertEquals("[8, 2, 3, 7]", c41.execute(8, 7).toString());
assertEquals("[8, 2, 7, 4]", c31.execute(8, 7).toString());
} catch (PolyglotException e) {
fail(e.getMessage() + " for \n" + out.toString());
}
}
@Test
public void wrongConstructorNameYieldsTypeError() {
try {
var create =
ctx.eval(
"enso",
"""
type N
False
materialize v:N = v.to_text
create n = N.materialize (~True)
""")
.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "create");
assertTrue("Can evaluate", create.canExecute());
var r = create.execute(42);
fail("Expecting an exception, not " + r);
} catch (PolyglotException e) {
assertTrue(
"Expecting type error, but got: " + e.getMessage(),
e.getMessage().contains("Type_Error"));
}
}
}

View File

@ -1030,6 +1030,16 @@ final class TreeToIr {
}
case null -> translateSyntaxError(tree, new Syntax.UnsupportedSyntax("Strange unary -"));
};
case Tree.UnaryOprApp un when "~".equals(un.getOpr().codeRepr()) -> {
var methodName = buildName(un.getRhs());
var methodRef = new Name.MethodReference(
Option.empty(),
methodName,
methodName.location(),
meta(), diag()
);
yield methodRef;
}
case Tree.TypeSignature sig -> {
var methodName = buildName(sig.getVariable());
var methodReference = new CallArgument.Specified(

View File

@ -15,6 +15,7 @@ import org.enso.interpreter.node.BaseNode;
import org.enso.interpreter.node.callable.dispatch.InvokeFunctionNode;
import org.enso.interpreter.node.callable.thunk.ThunkExecutorNode;
import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.callable.UnresolvedConstructor;
import org.enso.interpreter.runtime.callable.UnresolvedConversion;
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
@ -236,6 +237,12 @@ public abstract class InvokeCallableNode extends BaseNode {
}
}
@Specialization
public Object invokeDynamicConstructor(
UnresolvedConstructor symbol, VirtualFrame callerFrame, State state, Object[] arguments) {
return symbol.withArguments(invokeFunctionNode.getSchema(), arguments);
}
@Specialization
public Object invokeDynamicSymbol(
UnresolvedSymbol symbol, VirtualFrame callerFrame, State state, Object[] arguments) {

View File

@ -32,6 +32,7 @@ import org.enso.interpreter.node.expression.builtin.meta.TypeOfNode;
import org.enso.interpreter.node.expression.literal.LiteralNode;
import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.callable.Annotation;
import org.enso.interpreter.runtime.callable.UnresolvedConstructor;
import org.enso.interpreter.runtime.callable.UnresolvedConversion;
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition;
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition.ExecutionMode;
@ -41,6 +42,7 @@ import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.callable.function.FunctionSchema;
import org.enso.interpreter.runtime.data.EnsoMultiValue;
import org.enso.interpreter.runtime.data.Type;
import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.error.DataflowError;
import org.enso.interpreter.runtime.error.PanicException;
import org.enso.interpreter.runtime.error.PanicSentinel;
@ -88,7 +90,15 @@ public abstract class ReadArgumentCheckNode extends Node {
expectedTypeMessage = expectedTypeMessage();
}
var ctx = EnsoContext.get(this);
var msg = comment == null ? "expression" : comment;
Text msg;
if (v instanceof UnresolvedConstructor) {
msg = Text.create("Cannot find constructor {got} among {exp}");
} else {
var where = Text.create(comment == null ? "expression" : comment);
var exp = Text.create("expected ");
var got = Text.create(" to be {exp}, but got {got}");
msg = Text.create(exp, Text.create(where, got));
}
var err = ctx.getBuiltins().error().makeTypeErrorOfComment(expectedTypeMessage, v, msg);
throw new PanicException(err, this);
}
@ -254,6 +264,15 @@ public abstract class ReadArgumentCheckNode extends Node {
throw panicSentinel;
}
@Specialization
Object doUnresolvedConstructor(
VirtualFrame frame,
UnresolvedConstructor unresolved,
@Cached UnresolvedConstructor.ConstructNode construct) {
var state = Function.ArgumentsHelper.getState(frame.getArguments());
return construct.execute(frame, state, expectedType, unresolved);
}
@Specialization(rewriteOn = InvalidAssumptionException.class)
Object doCheckNoConversionNeeded(VirtualFrame frame, Object v)
throws InvalidAssumptionException {

View File

@ -54,7 +54,12 @@ public final class TypeToDisplayTextNode extends Node {
throw EnsoContext.get(this).raiseAssertionPanic(this, null, e);
}
} else {
return "a polyglot object";
try {
var res = iop.toDisplayString(value);
return iop.asString(res);
} catch (UnsupportedMessageException ex) {
return "a polyglot object";
}
}
}
}

View File

@ -3,14 +3,16 @@ package org.enso.interpreter.node.expression.constant;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.NodeInfo;
import org.enso.interpreter.node.ExpressionNode;
import org.enso.interpreter.runtime.callable.UnresolvedConstructor;
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
import org.enso.interpreter.runtime.data.EnsoObject;
/** Simple constant node that always results in the same {@link UnresolvedSymbol}. */
@NodeInfo(shortName = "DynamicSym")
public class DynamicSymbolNode extends ExpressionNode {
private final UnresolvedSymbol unresolvedSymbol;
private final EnsoObject unresolvedSymbol;
private DynamicSymbolNode(UnresolvedSymbol unresolvedSymbol) {
private DynamicSymbolNode(EnsoObject unresolvedSymbol) {
this.unresolvedSymbol = unresolvedSymbol;
}
@ -24,6 +26,10 @@ public class DynamicSymbolNode extends ExpressionNode {
return new DynamicSymbolNode(symbol);
}
public static DynamicSymbolNode build(UnresolvedConstructor symbol) {
return new DynamicSymbolNode(symbol);
}
/**
* Gets the dynamic symbol from the node.
*

View File

@ -195,8 +195,8 @@ public final class Error {
* @param comment description of the value that was being checked
* @return a runtime representation of the error.
*/
public Atom makeTypeErrorOfComment(Object expected, Object actual, String comment) {
return typeError.newInstance(expected, actual, Text.create(comment));
public Atom makeTypeErrorOfComment(Object expected, Object actual, Text comment) {
return typeError.newInstance(expected, actual, comment);
}
/**

View File

@ -0,0 +1,161 @@
package org.enso.interpreter.runtime.callable;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.MaterializedFrame;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage;
import com.oracle.truffle.api.nodes.Node;
import java.util.Arrays;
import java.util.Objects;
import org.enso.interpreter.node.callable.InvokeCallableNode.ArgumentsExecutionMode;
import org.enso.interpreter.node.callable.InvokeCallableNode.DefaultsExecutionMode;
import org.enso.interpreter.node.callable.dispatch.InvokeFunctionNode;
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
import org.enso.interpreter.runtime.data.EnsoObject;
import org.enso.interpreter.runtime.data.Type;
import org.enso.interpreter.runtime.data.atom.AtomConstructor;
import org.enso.interpreter.runtime.state.State;
/**
* Value representing a by-name identified constructor of a yet unknown {@link Type}. Create new
* instance by providing name to {@link #build(String)} method. Then apply arguments to it via
* {@link #withArguments(Object[])} method.
*
* <p>Let the object flow thru the interpreter and resolve it when the required {@link Type} is
* known.
*/
@ExportLibrary(InteropLibrary.class)
public final class UnresolvedConstructor implements EnsoObject {
private static final CallArgumentInfo[] NONE = new CallArgumentInfo[0];
private final String name;
final CallArgumentInfo[] descs;
private final Object[] args;
/**
* Creates a new unresolved name.
*
* @param name constructor name
* @param descs argument descriptions to apply to the constructor
* @param args argument values to apply to the constructor
*/
private UnresolvedConstructor(String name, CallArgumentInfo[] descs, Object[] args) {
this.name = name;
this.descs = descs;
this.args = args;
}
final String getName() {
return name;
}
@Override
@CompilerDirectives.TruffleBoundary
public String toString() {
return "~" + name;
}
@ExportMessage
String toDisplayString(boolean allowSideEffects) {
return toString();
}
/**
* Creates an instance of this node.
*
* @param name the name (of the constructor) we are searching for
* @return a object representing unresolved (constructor)
*/
public static UnresolvedConstructor build(String name) {
return new UnresolvedConstructor(name, NONE, NONE);
}
/**
* Marks this object as executable through the interop library.
*
* @param additionalDescriptions description of the applied arguments
* @param additionalArguments new arguments to add to the unresolved constructor
* @return always true @ExportMessage public boolean isExecutable() { return true; }
*/
public UnresolvedConstructor withArguments(
CallArgumentInfo[] additionalDescriptions, Object[] additionalArguments) {
if (this.args == NONE) {
return new UnresolvedConstructor(this.name, additionalDescriptions, additionalArguments);
} else {
var newDescs = join(this.descs, additionalDescriptions);
var newArgs = join(this.args, additionalArguments);
return new UnresolvedConstructor(this.name, newDescs, newArgs);
}
}
final UnresolvedConstructor asPrototype() {
return new UnresolvedConstructor(this.name, this.descs, null);
}
final boolean sameAsPrototyped(UnresolvedConstructor other) {
if (descs.length != other.descs.length) {
return false;
}
if (!name.equals(other.name)) {
return false;
}
for (var i = 0; i < descs.length; i++) {
if (!Objects.equals(descs[i].getName(), other.descs[i].getName())) {
return false;
}
}
return true;
}
private static <T> T[] join(T[] arr1, T[] arr2) {
var ret = Arrays.copyOf(arr1, arr1.length + arr2.length);
System.arraycopy(arr2, 0, ret, arr1.length, arr2.length);
return ret;
}
public abstract static class ConstructNode extends Node {
static final DefaultsExecutionMode EXEC_MODE = DefaultsExecutionMode.EXECUTE;
static final ArgumentsExecutionMode ARGS_MODE = ArgumentsExecutionMode.EXECUTE;
public abstract Object execute(
VirtualFrame frame, State state, Type expectedType, UnresolvedConstructor unresolved);
@Specialization(
guards = {"cachedType == expectedType", "prototype.sameAsPrototyped(unresolved)"},
limit = "10")
Object instantiateCached(
VirtualFrame frame,
State state,
Type expectedType,
UnresolvedConstructor unresolved,
@Cached("expectedType") Type cachedType,
@Cached("unresolved.asPrototype()") UnresolvedConstructor prototype,
@Cached("expectedType.getConstructors().get(prototype.getName())") AtomConstructor c,
@Cached("build(prototype.descs,EXEC_MODE,ARGS_MODE)") InvokeFunctionNode invoke) {
if (c == null) {
return null;
} else {
var fn = c.getConstructorFunction();
var r = invoke.execute(fn, frame, state, unresolved.args);
return r;
}
}
@Specialization(replaces = "instantiateCached")
@CompilerDirectives.TruffleBoundary
Object instantiateUncached(
MaterializedFrame frame, State state, Type expectedType, UnresolvedConstructor unresolved) {
var c = expectedType.getConstructors().get(unresolved.getName());
if (c == null) {
return null;
}
var fn = c.getConstructorFunction();
var invoke = InvokeFunctionNode.build(unresolved.descs, EXEC_MODE, ARGS_MODE);
var r = invoke.execute(fn, frame, state, unresolved.args);
return r;
}
}
}

View File

@ -97,6 +97,7 @@ import org.enso.interpreter.runtime.callable.function.{
Function => RuntimeFunction
}
import org.enso.interpreter.runtime.callable.{
UnresolvedConstructor,
UnresolvedConversion,
UnresolvedSymbol,
Annotation => RuntimeAnnotation
@ -1680,6 +1681,16 @@ class IrToTruffle(
UnresolvedSymbol.build(nameStr, moduleScope)
)
}
case Name.MethodReference(
None,
Name.Literal(nameStr, _, _, _, _, _),
_,
_,
_
) =>
DynamicSymbolNode.build(
UnresolvedConstructor.build(nameStr)
)
case Name.Self(location, _, passData, _) =>
processName(
Name.Literal(
@ -1715,10 +1726,6 @@ class IrToTruffle(
throw new CompilerError(
"Blanks should not be present at codegen time."
)
case _: Name.MethodReference =>
throw new CompilerError(
"Method references should not be present at codegen time."
)
case _: Name.Qualified =>
throw new CompilerError(
"Qualified names should not be present at codegen time."

View File

@ -15,6 +15,7 @@ import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.CommandLineOptionException;
import org.openjdk.jmh.runner.options.CommandLineOptions;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import org.openjdk.jmh.runner.options.TimeValue;
/** Runner class for the benchmarks. Discovers, runs and reports benchmark results. */
public class BenchmarksRunner {
@ -77,7 +78,12 @@ public class BenchmarksRunner {
private static Collection<RunResult> runCompileOnly(List<String> includes)
throws RunnerException {
System.out.println("Running benchmarks " + includes + " in compileOnly mode");
var optsBuilder = new OptionsBuilder().measurementIterations(1).warmupIterations(0).forks(0);
var optsBuilder =
new OptionsBuilder()
.measurementTime(TimeValue.seconds(1))
.measurementIterations(1)
.warmupIterations(0)
.forks(0);
includes.forEach(optsBuilder::include);
var opts = optsBuilder.build();
var runner = new Runner(opts);

View File

@ -2,6 +2,9 @@ from Standard.Base import all
import Standard.Base.Errors.Common.No_Such_Conversion
import Standard.Base.Errors.Common.Type_Error
import Standard.Base.Runtime.State
from Standard.Base.Errors.Common import Uninitialized_State
import project.Semantic.Conversion.Methods
import project.Semantic.Conversion.Types
import project.Semantic.Conversion_Use.Hello
@ -30,6 +33,9 @@ type My_Error
type Not_Foo
Value notfoo
type Stateful
Value v:Number=(State.get Number)
Foo.from (that:Bar) = Foo.Value that.bar
Foo.from (that:Baz) = Foo.Value that.baz
Foo.from (that:Text) = Foo.Value that.length
@ -427,6 +433,83 @@ add_specs suite_builder =
do_duration now
suite_builder.group "Autoscoped Constructors" group_builder->
group_builder.specify "Foo.Value as autoscoped" <|
v = ~Value 10
foo = v:Foo
Foo.Value 10 . should_equal foo
group_builder.specify "Autoscope to two different values" <|
v = ~Value 10
foo = v:Foo
bar = v:Bar
Foo.Value 10 . should_equal foo
Bar.Value 10 . should_equal bar
group_builder.specify "Cannot find constructor" <|
v = ~Value 10
b = Panic.recover Any <|
x = v:Back
x
b . should_fail_with Type_Error
msg = b.to_display_text
msg . should_contain "Cannot find constructor ~Value among Back"
group_builder.specify "Choose first constructor" <|
v = ~Value 10
m_foo (m:Foo|Bar|Back) = m
m_bar (m:Bar|Foo|Back) = m
m_back_foo (m:Back|Foo|Bar) = m
m_back_bar (m:Back|Bar|Foo) = m
m_foo v . should_equal <| Foo.Value 10
m_bar v . should_equal <| Bar.Value 10
m_back_foo v . should_equal <| Foo.Value 10
m_back_bar v . should_equal <| Bar.Value 10
group_builder.specify "Choose suitable constructor" <|
v = ~Times 10
m_foo (m:Foo|Bar) = m
m_bar (m:Bar|Foo) = m
m_back (m:Foo|Bar|Back) = m
Panic.recover Any (m_foo v) . should_fail_with Type_Error
Panic.recover Any (m_bar v) . should_fail_with Type_Error
m_back v . should_equal <| Back.Times 10
group_builder.specify "Lazy constructor with State" <|
v0 = ~Value
v1 = ~Value 33
State.run Number 42 <|
s42 = State.get Number
s42 . should_equal 42
v0:Stateful . should_equal <| Stateful.Value 42
v1:Stateful . should_equal <| Stateful.Value 33
v1:Stateful . should_equal <| Stateful.Value 33
err = Panic.recover Any <|
v = v0:Stateful
v
err . should_fail_with Uninitialized_State
group_builder.specify "Autoscope vector elements" <|
foo_vec (v:Vector) = v.map e->
e:Foo . foo
vec = [~Value 3, ~Value 4, ~Value 5]
foo_vec vec . should_equal [3, 4, 5]
suite_builder.group "Polyglot Argument" group_builder->
f1 (x : DateTimeFormatter) = x.to_text
f2 (x : Text | DateTimeFormatter) = case x of

View File

@ -294,7 +294,7 @@ add_specs suite_builder =
group_builder.specify "Panic if wrong types passed in element_columns" <|
t = data.table
r = Panic.recover Any (t.to_xml 1.23)
r.to_text.should_equal "(Error: (Type_Error.Error Vector | Text | Integer | Regex 1.23 '`element_columns`'))"
r.to_text.should_equal "(Error: Type error: expected `element_columns` to be Vector | Text | Integer | Regex, but got Float.)"
group_builder.specify "Panic if wrong types passed in element_columns vector" pending='Not working' <|
t = data.table
r = Panic.recover Any (t.to_xml [1.23])
@ -302,27 +302,27 @@ add_specs suite_builder =
group_builder.specify "Panic if wrong types passed in attribute_columns" <|
t = data.table
r = Panic.recover Any (t.to_xml [] 1.23)
r.to_text.should_equal "(Error: (Type_Error.Error Vector | Text | Integer | Regex 1.23 '`attribute_columns`'))"
r.to_text.should_equal "(Error: Type error: expected `attribute_columns` to be Vector | Text | Integer | Regex, but got Float.)"
group_builder.specify "Panic if wrong types passed in attribute_columns vector" pending='Not working' <|
t = data.table
r = Panic.recover Any (t.to_xml [] [1.23])
r.to_text.should_equal "(Error: (Type_Error.Error Vector | Text | Integer | Regex 1.23 '`attribute_columns`'))"
r.to_text.should_equal "(Error: Type error: expected `attribute_columns` to be Vector | Text | Integer | Regex, but got Float.)"
group_builder.specify "Panic if wrong types passed in value_column" <|
t = data.table
r = Panic.recover Any (t.to_xml [] [] 1.23)
r.to_text.should_equal "(Error: (Type_Error.Error Text | Integer | Nothing 1.23 '`value_column`'))"
r.to_text.should_equal "(Error: Type error: expected `value_column` to be Text | Integer | Nothing, but got Float.)"
group_builder.specify "Panic if wrong types passed in root_name" <|
t = data.table
r = Panic.recover Any (t.to_xml [] [] "Year" 1.23)
r.to_text.should_equal "(Error: (Type_Error.Error Text 1.23 '`root_name`'))"
r.to_text.should_equal "(Error: Type error: expected `root_name` to be Text, but got Float.)"
group_builder.specify "Panic if wrong types passed in row_name" <|
t = data.table
r = Panic.recover Any (t.to_xml [] [] "Year" "Table" 1.23)
r.to_text.should_equal "(Error: (Type_Error.Error Text 1.23 '`row_name`'))"
r.to_text.should_equal "(Error: Type error: expected `row_name` to be Text, but got Float.)"
group_builder.specify "Panic if wrong types passed in on_problems" <|
t = data.table
r = Panic.recover Any (t.to_xml [] [] "Year" "Table" "row" 1.23)
r.to_text.should_equal "(Error: (Type_Error.Error Problem_Behavior 1.23 '`on_problems`'))"
r.to_text.should_equal "(Error: Type error: expected `on_problems` to be Problem_Behavior, but got Float.)"
group_builder.specify "works with unicode characters" <|
unicode_column = ["unicode", ['\u00A9', "👩‍🔬"]]
# | unicode