Implement Generic Programming Facilities (#1216)

This commit is contained in:
Marcin Kostrzewa 2020-10-13 13:45:58 +02:00 committed by GitHub
parent 0a9e2a42ce
commit 2a44a858a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 460 additions and 9 deletions

View File

@ -3,4 +3,4 @@ from Base import all
## Returns the method name of the method that could not be found.
No_Such_Method_Error.method_name : Text
No_Such_Method_Error.method_name =
Meta.get_unresolved_symbol_name this.symbol
Meta.meta this.symbol . name

View File

@ -4,11 +4,13 @@ import Base.Number.Extensions
import Base.Text.Extensions
import Base.System.File
import Base.Meta.Enso_Project
import Base.Meta.Meta
import Base.Error.Extensions
import Base.Polyglot.Java
from Builtins import Unit, Number, Integer, Any, True, False
from Builtins import Unit, Number, Integer, Any, True, False, Cons
from Builtins export all
export Base.Meta.Meta
from Builtins export all hiding Meta
from Base.Meta.Enso_Project export all
from Base.List export Nil, Cons
@ -68,3 +70,26 @@ type Math
to its diameter.
Math.pi : Decimal
Math.pi = 3.141592653589793
## Generic equality of arbitrary values.
Any.== : Any -> Boolean
Any.== that = if Meta.is_same_object this that then True else
this_meta = Meta.meta this
that_meta = Meta.meta that
case Cons this_meta that_meta of
Cons (Meta.Atom _) (Meta.Atom _) ->
c_1 = this_meta.constructor
c_2 = that_meta.constructor
if not (Meta.is_same_object c_1 c_2) then False else
f_1 = this_meta.fields
f_2 = that_meta.fields
0.upto f_1.length . every i-> (f_1.at i) == (f_2.at i)
Cons (Meta.Error _) (Meta.Error _) -> this_meta.payload == that_meta.payload
Cons (Meta.Polyglot o_1) (Meta.Polyglot o_2) ->
langs_match = this_meta.language == Meta.Java && that_meta.language == Meta.Java
if not langs_match then False else o_1.equals [o_2]
## Constructor comparison is covered by the identity equality.
Primitive objects should define their own equality.
Therefore, there is no more cases to handle in this method.
_ -> False

View File

@ -0,0 +1,88 @@
from Base import all
import Builtins
## Represents a polyglot language.
type Language
## The Java laguage.
type Java
## Unknown language.
type Unknown
## A meta-representation of a runtime value.
! Warning
The functionality contained in this module exposes certain implementation
details of the language. As such, the API has no stability guarantees and
is subject to change as the Enso interpreter evolves.
type Meta
## An Atom meta-representation.
type Atom value
## A constructor meta-representation.
type Constructor value
## A primitive value meta-prepresentation.
type Primitive value
## An unresolved symbol meta-representation.
type Unresolved_Symbol value
## An error meta-representation.
type Error value
## A polyglot value meta-representation.
type Polyglot value
## Returns a vector of field values of the given atom.
Atom.fields : Vector
Atom.fields = Vector (Builtins.Meta.get_atom_fields this.value)
## Returns a constructor value of the given atom.
Atom.constructor : Any
Atom.constructor = Builtins.Meta.get_atom_constructor this.value
## Returns a new unresolved symbol with its name changed to the provided
argument.
Unresolved_Symbol.rename : Text -> Any
Unresolved_Symbol.rename new_name =
Builtins.Meta.create_unresolved_symbol new_name this.scope
## Returns the name of an unresolved symbol.
Unresolved_Symbol.name : Text
Unresolved_Symbol.name = Builtins.Meta.get_unresolved_symbol_name this.value
## Returns the definition scope of an unresolved symbol.
Unresolved_Symbol.scope : Any
Unresolved_Symbol.scope = Builtins.Meta.get_unresolved_symbol_scope this.value
## Returns the payload carried by an error value.
Error.payload : Any
Error.payload = this.value.catch e->e
## Returns a vector of field names defined by a constructor.
Constructor.fields : Vector
Constructor.fields = Vector (Builtins.Meta.get_constructor_fields this.value)
## Returns the name of a constructor.
Constructor.name : Text
Constructor.name = Builtins.Meta.get_constructor_name this.value
## Creates a new atom of the given constructor, with field values provided
in the `fields` vector.
Constructor.new : Vector -> Any
Constructor.new fields = Builtins.Meta.new_atom this.value fields.to_array
## Returns the language of a polyglot value.
Polyglot.get_language : Language
Polyglot.get_language =
lang_str = Builtins.Meta.get_polyglot_language
if lang_str == "java" then Java else Unknown
## Returns a meta-representation of a given runtime entity.
meta : Any -> Meta
meta value = if Builtins.Meta.is_atom value then Atom value else
if Builtins.Meta.is_constructor value then Constructor value else
if Builtins.Meta.is_polyglot value then Polyglot value else
if Builtins.Meta.is_unresolved_symbol value then Unresolved_Symbol value else
if Builtins.Meta.is_error value then Error value else
Primitive value
## Checks whether two objects are represented by the same underlying reference.
This is a power-user feature, that is only useful for certain optimizations.
is_same_object : Any -> Any -> Boolean
is_same_object value_1 value_2 = Builtins.Meta.is_same_object value_1 value_2

View File

@ -83,3 +83,11 @@ Integer.upto n = Range this n
Returns `True` when `this` and `that` are at most `epsilon` apart.
Number.equals : Number -> Number -> Boolean
Number.equals that epsilon=0.0 = (this - that).abs <= epsilon
## Returns the smaller value of `this` and `that`.
Number.min : Number -> Number
Number.min that = if this < that then this else that
## Returns the larger value of `this` and `that`.
Number.max : Number -> Number
Number.max that = if this > that then this else that

View File

@ -197,6 +197,22 @@ type Vector
take_right count = if count >= this.length then this else
this.drop (this.length - count)
## Performs a pair-wise operation passed in `function` on consecutive
elements of `this` and `that`.
The result of this function is a vector of length being the shorter of
`this` and `that`, containing results of calling `function`.
> Example
To pairwise-sum two vectors:
zip [1, 2, 3] [4, 5, 6] (+) == [5, 7, 9]
When the `function` is not provided, it defaults to creating a pair
of both elements:
zip [1, 2, 3] [4, 5, 6] == [[1, 4], [2, 5], [3, 6]]
zip : Vector -> (Any -> Any -> Any) -> Vector
zip that function=[_,_] =
len = min this.length that.length
Vector.new len i-> function (this.at i) (that.at i)
## A builder type for Enso vectors.
A vector builder is a mutable data structure, that allows to gather a

View File

@ -434,6 +434,8 @@ public abstract class MethodResolverNode extends Node {
return context.getBuiltins().polyglot().getPolyglotToTextFunction();
} else if (symbol.getName().equals("catch")) {
return symbol.resolveFor(context.getBuiltins().any());
} else if (symbol.getName().equals("==")) {
return symbol.resolveFor(context.getBuiltins().any());
} else {
return context.getBuiltins().polyglot().buildPolyglotMethodDispatch(symbol);
}

View File

@ -0,0 +1,17 @@
package org.enso.interpreter.node.expression.builtin.meta;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.callable.atom.Atom;
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
import org.enso.interpreter.runtime.data.Array;
@BuiltinMethod(
type = "Meta",
name = "get_atom_constructor",
description = "Gets the constructor of an atom.")
public class GetAtomConstructorNode extends Node {
AtomConstructor execute(Object _this, Atom atom) {
return atom.getConstructor();
}
}

View File

@ -0,0 +1,18 @@
package org.enso.interpreter.node.expression.builtin.meta;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
import org.enso.interpreter.runtime.callable.atom.Atom;
import org.enso.interpreter.runtime.data.Array;
import org.enso.interpreter.runtime.scope.ModuleScope;
@BuiltinMethod(
type = "Meta",
name = "get_atom_fields",
description = "Gets the fields of an unresolved atom.")
public class GetAtomFieldsNode extends Node {
Array execute(Object _this, Atom atom) {
return new Array(atom.getFields());
}
}

View File

@ -0,0 +1,24 @@
package org.enso.interpreter.node.expression.builtin.meta;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition;
import org.enso.interpreter.runtime.callable.atom.Atom;
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
import org.enso.interpreter.runtime.data.Array;
import org.enso.interpreter.runtime.data.text.Text;
@BuiltinMethod(
type = "Meta",
name = "get_constructor_fields",
description = "Gets the field names of a constructor.")
public class GetConstructorFieldNamesNode extends Node {
Array execute(Object _this, AtomConstructor atom_constructor) {
ArgumentDefinition[] fields = atom_constructor.getFields();
Object[] result = new Object[fields.length];
for (int i = 0; i < fields.length; i++) {
result[i] = Text.create(fields[i].getName());
}
return new Array(result);
}
}

View File

@ -0,0 +1,17 @@
package org.enso.interpreter.node.expression.builtin.meta;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.callable.atom.Atom;
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
import org.enso.interpreter.runtime.data.text.Text;
@BuiltinMethod(
type = "Meta",
name = "get_constructor_name",
description = "Gets the name of a constructor.")
public class GetConstructorNameNode extends Node {
Text execute(Object _this, AtomConstructor atom_constructor) {
return Text.create(atom_constructor.getName());
}
}

View File

@ -0,0 +1,34 @@
package org.enso.interpreter.node.expression.builtin.meta;
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.data.text.Text;
@BuiltinMethod(
type = "Meta",
name = "get_polyglot_language",
description = "Returns a text representation of a language of origin of a given value.")
public abstract class GetPolyglotLanguageNode extends Node {
static GetPolyglotLanguageNode build() {
return GetPolyglotLanguageNodeGen.create();
}
private final Text java = Text.create("java");
private final Text unknown = Text.create("unknown");
abstract Text execute(Object _this, Object value);
@Specialization
Text doExecute(Object _this, Object value, @CachedContext(Language.class) Context context) {
if (context.getEnvironment().isHostObject(value)) {
return java;
} else {
return unknown;
}
}
}

View File

@ -0,0 +1,15 @@
package org.enso.interpreter.node.expression.builtin.meta;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.type.TypesGen;
@BuiltinMethod(
type = "Meta",
name = "is_atom_constructor",
description = "Checks if the argument is a constructor.")
public class IsAtomConstructorNode extends Node {
boolean execute(Object _this, Object value) {
return TypesGen.isAtomConstructor(value);
}
}

View File

@ -0,0 +1,15 @@
package org.enso.interpreter.node.expression.builtin.meta;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.type.TypesGen;
@BuiltinMethod(
type = "Meta",
name = "is_atom",
description = "Checks if the argument is an atom")
public class IsAtomNode extends Node {
boolean execute(Object _this, Object value) {
return TypesGen.isAtom(value);
}
}

View File

@ -0,0 +1,15 @@
package org.enso.interpreter.node.expression.builtin.meta;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.type.TypesGen;
@BuiltinMethod(
type = "Meta",
name = "is_error",
description = "Checks if the argument is an error.")
public class IsErrorNode extends Node {
boolean execute(Object _this, Object value) {
return TypesGen.isRuntimeError(value);
}
}

View File

@ -0,0 +1,23 @@
package org.enso.interpreter.node.expression.builtin.meta;
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.type.TypesGen;
@BuiltinMethod(type = "Meta", name = "is_polyglot", description = "Checks if the argument is a polyglot value.")
public abstract class IsPolyglotNode extends Node {
static IsPolyglotNode build() {
return IsPolyglotNodeGen.create();
}
abstract boolean execute(Object _this, Object value);
@Specialization
boolean doExecute(Object _this, Object value, @CachedContext(Language.class) Context context) {
return context.getEnvironment().isHostObject(value);
}
}

View File

@ -0,0 +1,15 @@
package org.enso.interpreter.node.expression.builtin.meta;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.type.TypesGen;
@BuiltinMethod(
type = "Meta",
name = "is_same_object",
description = "Checks if the two arguments share an underlying reference.")
public class IsSameObjectNode extends Node {
boolean execute(Object _this, Object value_1, Object value_2) {
return value_1 == value_2;
}
}

View File

@ -0,0 +1,15 @@
package org.enso.interpreter.node.expression.builtin.meta;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.type.TypesGen;
@BuiltinMethod(
type = "Meta",
name = "is_unresolved_symbol",
description = "Checks if the argument is an unresolved symbol.")
public class IsUnresolvedSymbolNode extends Node {
boolean execute(Object _this, Object value) {
return TypesGen.isUnresolvedSymbol(value);
}
}

View File

@ -0,0 +1,18 @@
package org.enso.interpreter.node.expression.builtin.meta;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.callable.atom.Atom;
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
import org.enso.interpreter.runtime.data.Array;
import org.enso.interpreter.runtime.type.TypesGen;
@BuiltinMethod(
type = "Meta",
name = "new_atom",
description = "Creates a new atom with given constructor and fields.")
public class NewAtomInstanceNode extends Node {
Atom execute(Object _this, AtomConstructor constructor, Array fields) {
return constructor.newInstance(fields.getItems());
}
}

View File

@ -1,9 +1,7 @@
package org.enso.interpreter.runtime.builtin;
import org.enso.interpreter.Language;
import org.enso.interpreter.node.expression.builtin.meta.CreateUnresolvedSymbolMethodGen;
import org.enso.interpreter.node.expression.builtin.meta.GetUnresolvedSymbolNameMethodGen;
import org.enso.interpreter.node.expression.builtin.meta.GetUnresolvedSymbolScopeMethodGen;
import org.enso.interpreter.node.expression.builtin.meta.*;
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
import org.enso.interpreter.runtime.scope.ModuleScope;
@ -20,6 +18,9 @@ public class Meta {
AtomConstructor meta = new AtomConstructor("Meta", scope).initializeFields();
scope.registerConstructor(meta);
scope.registerMethod(
meta, "is_unresolved_symbol", IsUnresolvedSymbolMethodGen.makeFunction(language));
scope.registerMethod(
meta,
"get_unresolved_symbol_name",
@ -30,5 +31,25 @@ public class Meta {
GetUnresolvedSymbolScopeMethodGen.makeFunction(language));
scope.registerMethod(
meta, "create_unresolved_symbol", CreateUnresolvedSymbolMethodGen.makeFunction(language));
scope.registerMethod(meta, "is_constructor", IsAtomConstructorMethodGen.makeFunction(language));
scope.registerMethod(
meta, "get_constructor_name", GetConstructorNameMethodGen.makeFunction(language));
scope.registerMethod(
meta, "get_constructor_fields", GetConstructorFieldNamesMethodGen.makeFunction(language));
scope.registerMethod(meta, "new_atom", NewAtomInstanceMethodGen.makeFunction(language));
scope.registerMethod(meta, "is_atom", IsAtomMethodGen.makeFunction(language));
scope.registerMethod(meta, "get_atom_fields", GetAtomFieldsMethodGen.makeFunction(language));
scope.registerMethod(
meta, "get_atom_constructor", GetAtomConstructorMethodGen.makeFunction(language));
scope.registerMethod(meta, "is_error", IsErrorMethodGen.makeFunction(language));
scope.registerMethod(meta, "is_polyglot", IsPolyglotMethodGen.makeFunction(language));
scope.registerMethod(meta, "get_polyglot_language", GetPolyglotLanguageMethodGen.makeFunction(language));
scope.registerMethod(meta, "is_same_object", IsSameObjectMethodGen.makeFunction(language));
}
}

View File

@ -196,8 +196,18 @@ public class AtomConstructor implements TruffleObject {
return newInstance(arguments);
}
@ExportMessage
String toDisplayString(boolean allowSideEffects) {
return "Constructor<" + name + ">";
}
/** @return the fully qualified name of this constructor. */
public QualifiedName getQualifiedName() {
return definitionScope.getModule().getName().createChild(getName());
}
/** @return the fields defined by this constructor. */
public ArgumentDefinition[] getFields() {
return constructorFunction.getSchema().getArgumentInfos();
}
}

View File

@ -148,10 +148,12 @@ case object ShadowedPatternFields extends IRPass {
name = IR.Name.Blank(location = name.location)
)
.addDiagnostic(warning)
} else {
} else if (!name.isInstanceOf[IR.Name.Blank]) {
lastSeen(name.name) = named
seenNames += name.name
named
} else {
named
}
case cons @ Pattern.Constructor(_, fields, _, _, _) =>
val newFields = fields.reverse.map(go(_, seenNames)).reverse

View File

@ -152,6 +152,10 @@ case object LambdaConsolidate extends IRPass {
val (processedArgList, newBody) =
computeReplacedExpressions(newArgNames, lastBody, usageIdsForShadowed)
val consolidatedArgs = processedArgList.map(
_.mapExpressions(runExpression(_, inlineContext))
)
val newLocation = chainedLambdas.head.location match {
case Some(location) =>
Some(
@ -167,7 +171,7 @@ case object LambdaConsolidate extends IRPass {
}
lam.copy(
arguments = processedArgList,
arguments = consolidatedArgs,
body = runExpression(newBody, inlineContext),
location = newLocation,
canBeTCO = chainedLambdas.last.canBeTCO

View File

@ -198,6 +198,17 @@ class LambdaConsolidateTest extends CompilerTest {
.suspended shouldEqual true
}
"work properly with arguments defaulted to lambdas" in {
implicit val inlineContext: InlineContext = mkContext
val ir = """
|x -> (y = x->y->z) -> y x
|""".stripMargin.preprocessExpression.get.optimise.asInstanceOf[IR.Function.Lambda]
ir.arguments.length shouldEqual 2
val defaultExpr = ir.arguments(1).defaultValue.get
defaultExpr shouldBe a[IR.Function.Lambda]
defaultExpr.asInstanceOf[IR.Function.Lambda].arguments.length shouldEqual 2
}
"collapse lambdas with multiple parameters" in {
implicit val inlineContext: InlineContext = mkContext

View File

@ -1,4 +1,5 @@
from Base import all
import Builtins
import Base.Bench_Utils
gen_list len = 0.upto len . fold Nil (l -> i -> Cons i+1 l)
@ -11,11 +12,22 @@ sum_vec vec =
res = sumator 0 0
res
sum_list_meta list =
nil_cons = Meta.meta Nil . constructor
folder acc list =
meta_list = Meta.meta list
if meta_list.constructor == nil_cons then acc else
fs = meta_list.fields
folder (acc + fs.at 0) (fs.at 1)
res = folder 0 list
res
main =
mil = 1000000
list = here.gen_list mil
vec = Vector.new mil (ix -> ix + 1)
vec_decimal = Vector.new mil (ix -> ix + 0.0)
Bench_Utils.measure (vec.fold 0 (+)) "vector fold" 1000 10
Bench_Utils.measure (here.sum_list_meta list) "list meta-fold" 1000 10
Bench_Utils.measure (list.fold 0 (+)) "list fold" 1000 10
Bench_Utils.measure (vec.fold 0 (+)) "vector fold" 1000 10
Bench_Utils.measure (vec_decimal.fold 0 (+)) "vector decimal fold" 1000 10

View File

@ -5,6 +5,7 @@ import Test.Semantic.Deep_Export.Spec as Deep_Export_Spec
import Test.Semantic.Java_Interop_Spec
import Test.Semantic.Error_Spec
import Test.Semantic.Names_Spec
import Test.Semantic.Meta_Spec
import Test.List_Spec
import Test.Number_Spec
@ -29,3 +30,4 @@ main = Test.Suite.runMain <|
Text_Spec.spec
Time_Spec.spec
File_Spec.spec
Meta_Spec.spec

View File

@ -0,0 +1,24 @@
from Base import all
import Base.Test
type My_Type foo bar baz
My_Type.my_method = this.foo + this.bar + this.baz
spec = describe "Meta-Value Manipulation" <|
it "should allow manipulating unresolved symbols" <|
sym = does_not_exist
meta_sym = Meta.meta sym
meta_sym.name.should equal "does_not_exist"
new_sym = meta_sym . rename "my_method"
object = My_Type 1 2 3
new_sym object . should equal 6
it "should allow manipulating atoms" <|
atom = My_Type 1 "foo" Unit
meta_atom = Meta.meta atom
meta_atom.constructor.should equal My_Type
meta_atom.fields.should equal [1, "foo", Unit]
Meta.meta (meta_atom.constructor) . new [1, "foo", Unit] . should equal atom
it "should correctly return representations of different classes of objects" <|
Meta.meta 1 . should equal (Meta.Primitive 1)
Meta.meta "foo" . should equal (Meta.Primitive "foo")