Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Meta.get_annotation and extension methods #7000

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ type No_Such_Method
Convert the No_Such_Method error to a human-readable format.
to_display_text : Text
to_display_text self =
target_type_name = if Meta.is_polyglot self.target then self.target.to_display_text else (Meta.type_of self.target).to_display_text
target_type_name = if Meta.meta self.target . is_a Meta.Polyglot then self.target.to_display_text else (Meta.type_of self.target).to_display_text
"Method `"+self.method_name+"` of type "+target_type_name+" could not be found."

@Builtin_Type
Expand Down
86 changes: 25 additions & 61 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Meta.enso
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import project.Data.Time.Time_Of_Day.Time_Of_Day
import project.Data.Time.Time_Zone.Time_Zone
import project.Data.Vector.Vector
import project.Nothing.Nothing
import project.Function.Function as Std_Function
import project.Polyglot.Java

import project.Error.Error as Base_Error
Expand Down Expand Up @@ -217,6 +218,20 @@ type Polyglot
lang_str = get_polyglot_language self.value
if lang_str == "java" then Language.Java else Language.Unknown

type Function
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are many type in Meta - why don't we have a single Meta type and many constructors Atom, Constructor, Primitive, etc.? Is this just a heritage or is there a benefit of doing so?

Vote +1 for me to attempt to rewrite to single Meta type and constructors. Vote -1 for keeping status quo as much as possible. CCing @jdunkerley, @GregoryTravis, @radeusgd

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not strongly attached to either way, although I think the current state of things is fine.

I think the reason could have been that each 'variant' has a different set of operations available on it - an Atom may have a fields field, but this does not make sense for a Function or Primitive. Ofc we can just throw 'Illegal_State' for such wrong calls but IMO this does not look good - it's better when the API of each 'variant' is visible from its type not from for what it works and for what it throws.

On the other hand, I'd appreciate some 'common parent type' for all these, but without class hierarchies / interfaces, I don't think we can have the cake (common type) and eat it too (clear, variant-specific API). Do you think it's somehow possible?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the common type is the problem. There is a really long signature and I'd love to shorten it to meta : Any -> Meta.

an just throw 'Illegal_State' for such wrong calls

Right now we are getting No_Such_Method errors. Rather than throwing the (or another) error, I'd just return some default, when sensible. If I rewrote to Meta with multiple constructors.

## PRIVATE
Value value

## PRIVATE
ADVANCED

Given a type object, method name and a parameter name, return the associated annotation if it exists.

Arguments:
- parameter_name: The name of the parameter to get the attribute for.
get_annotation : Text -> Any
get_annotation self parameter_name = Meta.get_annotation self.value... Nothing parameter_name

## ADVANCED

Checks whether `self` represents the same underlying reference as `value`.
Expand Down Expand Up @@ -365,14 +380,16 @@ atom_with_hole factory = @Builtin_Method "Meta.atom_with_hole_builtin"

Arguments:
- value: The runtime entity to get the meta representation of.
meta : Any -> Atom | Constructor | Primitive | Polyglot | Unresolved_Symbol | Error
meta value = if is_atom value then Atom.Value value else
if is_atom_constructor value then Constructor.Value value else
if is_polyglot value then Polyglot.Value value else
if is_unresolved_symbol value then Unresolved_Symbol.Value value else
if is_error value then Error.Value value.catch else
if is_type value then Type.Value value.catch else
Primitive.Value value
meta : Any -> Atom | Constructor | Primitive | Polyglot | Unresolved_Symbol | Error | Function
meta value =
fac_vec = [ Function.Value, Atom.Value, Constructor.Value, Polyglot.Value, Unresolved_Symbol.Value, Error.Value, Type.Value, Primitive.Value ]
factories = fac_vec.to_array
case meta_builtin -1 value... factories of
index : Integer -> meta_builtin index value.catch factories
ret -> ret

## PRIVATE
meta_builtin index value factories = @Builtin_Method "Meta.meta_builtin"

## PRIVATE
ADVANCED
Expand Down Expand Up @@ -439,59 +456,6 @@ type Language
An unknown language.
Unknown

## PRIVATE

Checks if the provided value is an atom constructor.

Arguments:
- value: The value to check.
is_atom_constructor : Any -> Boolean
is_atom_constructor value = @Builtin_Method "Meta.is_atom_constructor"

## PRIVATE

Checks if the provided value is an atom.

Arguments:
- value: The value to check.
is_atom : Any -> Boolean
is_atom value = @Builtin_Method "Meta.is_atom"

## PRIVATE

Checks if the provided value is a runtime error.

Arguments:
- value: The value to check.
is_error : Any -> Boolean
is_error value = @Builtin_Method "Meta.is_error"

## PRIVATE

Checks if the provided value is a type.

Arguments:
- value: The value to check.
is_type : Any -> Boolean
is_type value = @Builtin_Method "Meta.is_type"

## PRIVATE

Checks if the provided value is a polyglot value.

Arguments:
- value: The value to check.
is_polyglot : Any -> Boolean
is_polyglot value = @Builtin_Method "Meta.is_polyglot"

## PRIVATE

Checks if the provided value is an unresolved symbol.

Arguments:
- value: The value to check.
is_unresolved_symbol : Any -> Boolean
is_unresolved_symbol value = @Builtin_Method "Meta.is_unresolved_symbol"

## PRIVATE

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package org.enso.interpreter.node.expression.builtin.meta;

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.VirtualFrame;
import com.oracle.truffle.api.library.CachedLibrary;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.node.BaseNode;
import org.enso.interpreter.node.callable.thunk.ThunkExecutorNode;
Expand All @@ -13,12 +18,6 @@
import org.enso.interpreter.runtime.scope.ModuleScope;
import org.enso.interpreter.runtime.state.State;

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.VirtualFrame;
import com.oracle.truffle.api.library.CachedLibrary;

@BuiltinMethod(
type = "Meta",
name = "get_annotation",
Expand All @@ -29,6 +28,27 @@ public abstract class GetAnnotationNode extends BaseNode {
abstract Object execute(
VirtualFrame frame, State state, Object target, Object method, Object parameter);

@Specialization
Object doExecute(
VirtualFrame frame,
State state,
Function methodFunction,
Object method,
Object parameter,
@CachedLibrary(limit = "3") TypesLibrary types,
@Cached ThunkExecutorNode thunkExecutorNode,
@Cached ExpectStringNode expectStringNode
) {
String parameterName = expectStringNode.execute(parameter);
Annotation annotation = methodFunction.getSchema().getAnnotation(parameterName);
if (annotation != null) {
Function thunk =
Function.thunk(annotation.getExpression().getCallTarget(), frame.materialize());
return thunkExecutorNode.executeThunk(frame, thunk, state, getTailStatus());
}
return EnsoContext.get(this).getNothing();
}

@Specialization
Object doExecute(
VirtualFrame frame,
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package org.enso.interpreter.node.expression.builtin.meta;

import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.AcceptsError;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.EnsoContext;
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 = "meta_builtin",
description = "Find the type and constructs a Meta instances.",
autoRegister = false)
public class MetaBuiltinNode extends Node {
Object execute(long index, @AcceptsError Object value, Array factories) {
if (index < 0) {
index = findIndex(value);
}
if (index < 0) {
return -index;
}
var factory = (AtomConstructor) factories.getItems()[(int) index];
return factory.newInstance(value);
}

private int findIndex(Object value) {
if (TypesGen.isFunction(value)) {
return 0;
}
if (value instanceof Atom) {
return 1;
}
if (TypesGen.isAtomConstructor(value)) {
return 2;
}
if (TypesGen.isUnresolvedSymbol(value) || TypesGen.isUnresolvedConversion(value)) {
return 4;
}
if (TypesGen.isDataflowError(value)) {
return -5;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These indices look rather magical, I think we should have some comment explaining where they come from.

Why is the dataflow index the only negative one?

Copy link
Member Author

@JaroslavTulach JaroslavTulach Jun 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the relevant commit to read: 133ba8d

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be good to have some comment explaining this logic, as it's not that easy to understand it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the result of Meta.meta (Error.throw "Foo")?

IIRC it used to be Error.Value, but how I understand the current code, I'd guess it to be Primitive.Value (for Text). Do I understand it correctly?

}
if (TypesGen.isType(value)) {
return 6;
}
var ctx = EnsoContext.get(this);
if (ctx.getEnvironment().isHostObject(value)) {
return 3;
}
// primitive value
return 7;
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,7 @@
package org.enso.interpreter.runtime.scope;

import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.interop.TruffleObject;
import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.Module;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.data.Type;
import org.enso.interpreter.runtime.error.RedefinedMethodException;
import org.enso.interpreter.runtime.error.RedefinedConversionException;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
Expand All @@ -18,6 +10,12 @@
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.Module;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.data.Type;
import org.enso.interpreter.runtime.error.RedefinedConversionException;
import org.enso.interpreter.runtime.error.RedefinedMethodException;

/** A representation of Enso's per-file top-level scope. */
public final class ModuleScope implements TruffleObject {
Expand Down Expand Up @@ -194,11 +192,14 @@ public Function lookupMethodDefinition(Type type, String name) {
return definedHere;
}

return imports.stream()
.map(scope -> scope.getExportedMethod(type, name))
.filter(Objects::nonNull)
.findFirst()
.orElse(null);
for (var scope : imports) {
var method = scope.getExportedMethod(type, name);
if (method != null) {
return method;
}
}

return null;
}

@TruffleBoundary
Expand Down
Loading