Skip to content

Commit

Permalink
Use Meta.is_same_object to test type equality
Browse files Browse the repository at this point in the history
Removed `is_of_type` and moved some of the logic to `is_same_object`.
Turns out the latter was actually broken when comparing polyglot values
with the same underlying java class.

Additionally, discovered why I can't simply use `==` - the dispatch for
types is broken and it's a known "feature". This will be improved in the
upcoming statics change so I'm not going to duplicate that work. Once
in, we can most likely get rid of `should_equal_type` and
`should_not_equal_type`.

Addressed comments re date/time-related branches in pattern matching so
that it doesn't try to do semi-subtyping checks but rather checks for
equality.

Moved all logic of `type_of` to the builtin node.
Added tests for `type_of` of Date, Date_Time, Time_Of_Day and Time_Zone.
Might still change `type_of` to ensure that java classes aren't leaking.
  • Loading branch information
hubertp committed Sep 26, 2022
1 parent 2fb06fd commit c18bc0a
Show file tree
Hide file tree
Showing 16 changed files with 225 additions and 154 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@
- [Implemented `start_of` and `end_of` methods for date/time types allowing to
find start and end of a period of time containing the provided time.][3695]
- [Implemented `type_of` and `is_of_type` methods for getting the type of a
value and comparing types, respectively][3722]
value and comparing types, respectively.][3722]
- [Implemented `work_days_until` for counting work dys between dates and
`add_work_days` which allows to shift a date by a number of work days.][3726]
- [Added `query` and `read` functions to Database connections.][3727]
Expand Down
61 changes: 17 additions & 44 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Meta.enso
Original file line number Diff line number Diff line change
Expand Up @@ -253,12 +253,13 @@ is_same_object value_1 value_2 = @Builtin_Method "Meta.is_same_object"
## UNSTABLE
ADVANCED

Checks if `self` is an instance of `typ`.
Checks whether `self` represents the same underlying reference as `value`.

Arguments:
- typ: The type to check `self` against.
Any.is_a : Any -> Boolean
Any.is_a self typ = is_a self typ
- value_1: The first value.
- value_2: The second value.
Any.is_same_object_as : Any -> Boolean
Any.is_same_object_as self value = is_same_object self value

## UNSTABLE
ADVANCED
Expand All @@ -267,19 +268,18 @@ Any.is_a self typ = is_a self typ

Arguments:
- typ: The type to check `self` against.
Any.is_an : Any -> Boolean
Any.is_an self typ = is_a self typ
Any.is_a : Any -> Boolean
Any.is_a self typ = is_a self typ

## UNSTABLE
ADVANCED
PRIVATE

Checks if `self` type matches `typ` type.
Checks if `self` is an instance of `typ`.

Arguments:
- typ: The type to check `self` against.
Any.is_of_type : Any -> Boolean
Any.is_of_type self typ = is_of_type self typ
Any.is_an : Any -> Boolean
Any.is_an self typ = is_a self typ

## UNSTABLE
ADVANCED
Expand Down Expand Up @@ -314,17 +314,17 @@ is_a value typ = if is_same_object value typ then True else
if typ == Any then True else
if is_error value then typ == Base.Error else
case value of
Vector.Vector -> typ.is_of_type Vector.Vector
Array -> typ.is_of_type Array
Vector.Vector -> typ.is_same_object_as Vector.Vector
Array -> typ.is_same_object_as Array
Boolean -> if typ == Boolean then True else value == typ
Text -> typ == Text
Number -> if typ == Number then True else case value of
Integer -> typ == Integer
Decimal -> typ == Decimal
Date_Time.Date_Time -> typ.is_of_type Date_Time.Date_Time
Date.Date -> typ.is_of_type Date.Date
Time_Of_Day.Time_Of_Day -> typ.is_of_type Time_Of_Day.Time_Of_Day
Time_Zone.Time_Zone -> typ.is_of_type Time_Zone.Time_Zone
Date_Time.Date_Time -> typ.is_same_object_as Date_Time.Date_Time
Date.Date -> typ.is_same_object_as Date.Date
Time_Of_Day.Time_Of_Day -> typ.is_same_object_as Time_Of_Day.Time_Of_Day
Time_Zone.Time_Zone -> typ.is_same_object_as Time_Zone.Time_Zone
Base.Polyglot ->
typ==Base.Polyglot || java_instance_check value typ
_ ->
Expand Down Expand Up @@ -367,34 +367,7 @@ is_an value typ = is_a value typ
Arguments:
- value: the value to get the type of.
type_of : Any -> Any
type_of value =
if is_error value then Base.Error else
case value of
Integer -> Integer
Decimal -> Decimal
Number -> Number
_ ->
Meta.type_of_builtin value

## UNSTABLE
ADVANCED
PRIVATE

Checks if the given type agrees with the expected one

Arguments:
- tpe: the type to check
- expected: the expected type
is_of_type : Any -> Any -> Boolean
is_of_type tpe expected = @Builtin_Method "Meta.is_of_type_builtin"

## PRIVATE

A builtin method returning the type of a (polyglot or non-polyglot) value.

Arguments:
- value: the value to get the type of.
type_of_builtin value = @Builtin_Method "Meta.type_of_builtin"
type_of value = @Builtin_Method "Meta.type_of_builtin"

## Represents a polyglot language.
type Language
Expand Down
44 changes: 44 additions & 0 deletions distribution/lib/Standard/Test/0.0.0-dev/src/Main.enso
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,28 @@ Any.should_equal self that frames_to_skip=0 = case self == that of
msg = self.to_text + " did not equal " + that.to_text + " (at " + loc + ")."
fail msg

## Asserts that `self` value is equal to the expected type value.

Arguments:
- that: The type to check `self` for equality with.
- frames_to_skip (optional, advanced): used to alter the location which is
displayed as the source of this error.

> Example
Assert that some type is equal to another.,

import Standard.Examples
import Standard.Test

example_should_equal = Examples.some_tpe . should_equal_tpe Vector.Vector
Any.should_equal_type : Any -> Integer -> Assertion
Any.should_equal_type self that frames_to_skip=0 = case (self.is_same_object_as that) of
True -> Success
False ->
loc = Meta.get_source_location 2+frames_to_skip
msg = self.to_text + " did not equal type " + that.to_text + " (at " + loc + ")."
fail msg

## Asserts that `self` value is not equal to the expected value.

Arguments:
Expand All @@ -308,6 +330,28 @@ Any.should_not_equal self that frames_to_skip=0 = case self != that of
msg = self.to_text + " did equal " + that.to_text + " (at " + loc + ")."
fail msg

## Asserts that `self` value is not equal to the expected type value.

Arguments:
- that: The type to check `self` for equality with.
- frames_to_skip (optional, advanced): used to alter the location which is
displayed as the source of this error.

> Example
Assert that some type is equal to another.,

import Standard.Examples
import Standard.Test

example_should_not_equal = Examples.some_tpe . should_not_equal_tpe Vector.Vector
Any.should_not_equal_type : Any -> Integer -> Assertion
Any.should_not_equal_type self that frames_to_skip=0 = case (self.is_same_object_as that . not) of
True -> Success
False ->
loc = Meta.get_source_location 2+frames_to_skip
msg = self.to_text + " did equal type " + that.to_text + " (at " + loc + ")."
fail msg

## Asserts that `self` value is equal to the expected value.

Arguments:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ void doEnsoDate(VirtualFrame frame, Object state, EnsoDate date) {
accept(frame, state, new Object[0]);
}

@Specialization(guards = "interop.isDate(date)")
@Specialization(guards = {"interop.isDate(date)", "!interop.isTime(date)"})
void doDate(
VirtualFrame frame,
Object state,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ void doEnsoDateTime(VirtualFrame frame, Object state, EnsoDateTime dateTime) {
guards = {
"interop.isDate(dateTime)",
"interop.isTime(dateTime)",
"interop.isTimeZone(dateTime)"
})
void doDateTime(
VirtualFrame frame,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ void doEnsoTimeOfDay(VirtualFrame frame, Object state, EnsoTimeOfDay timeOfDay)
accept(frame, state, new Object[0]);
}

@Specialization(guards = "interop.isTime(timeOfDay)")
@Specialization(guards = {"!interop.isDate(timeOfDay)", "interop.isTime(timeOfDay)"})
void doTime(
VirtualFrame frame,
Object state,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ void doEnsoZone(VirtualFrame frame, Object state, EnsoTimeZone timeZone) {
accept(frame, state, new Object[0]);
}

@Specialization(guards = "interop.isTimeZone(zone)")
@Specialization(
guards = {"!interop.isDate(zone)", "!interop.isTime(zone)", "interop.isTimeZone(zone)"})
void doZone(
VirtualFrame frame,
Object state,
Expand Down

This file was deleted.

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

import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.AcceptsError;
import org.enso.interpreter.dsl.BuiltinMethod;
Expand All @@ -8,8 +11,20 @@
type = "Meta",
name = "is_same_object",
description = "Checks if the two arguments share an underlying reference.")
public class IsSameObjectNode extends Node {
boolean execute(@AcceptsError Object value_1, @AcceptsError Object value_2) {
return value_1 == value_2;
public abstract class IsSameObjectNode extends Node {

static IsSameObjectNode build() {
return IsSameObjectNodeGen.create();
}

abstract boolean execute(@AcceptsError Object left, @AcceptsError Object right);

@Specialization(limit = "3")
boolean doExecute(
Object left,
Object right,
@CachedLibrary("left") InteropLibrary leftInterop,
@CachedLibrary("right") InteropLibrary rightInteropp) {
return (left == right) || leftInterop.isIdentical(left, right, rightInteropp);
}
}
Original file line number Diff line number Diff line change
@@ -1,44 +1,79 @@
package org.enso.interpreter.node.expression.builtin.meta;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.profiles.BranchProfile;
import org.enso.interpreter.Constants;
import org.enso.interpreter.dsl.AcceptsError;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.builtin.Builtins;
import org.enso.interpreter.runtime.error.PanicException;
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
import org.enso.interpreter.runtime.number.EnsoBigInteger;
import org.enso.interpreter.runtime.type.TypesGen;

@BuiltinMethod(
type = "Meta",
name = "type_of_builtin",
description = "Returns the type of a value.")
public class TypeOfNode extends Node {
private @Child InteropLibrary library =
InteropLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH);
private @Child TypesLibrary types =
TypesLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH);

Object execute(Object value) {
if (library.hasMetaObject(value)) {
try {
return library.getMetaObject(value);
} catch (UnsupportedMessageException e) {
CompilerDirectives.transferToInterpreter();
Builtins builtins = Context.get(this).getBuiltins();
throw new PanicException(
builtins.error().makeTypeError(builtins.any(), value, "object"), this);
}
} else {
if (types.hasType(value)) {
return types.getType(value);
} else {
Context ctx = Context.get(this);
return ctx.getBuiltins().any();
}
public abstract class TypeOfNode extends Node {

abstract Object execute(@AcceptsError Object value);

static TypeOfNode build() {
return TypeOfNodeGen.create();
}

@Specialization
Object doDouble(double value) {
Context ctx = Context.get(this);
return ctx.getBuiltins().number().getDecimal();
}

@Specialization
Object doLong(long value) {
Context ctx = Context.get(this);
return ctx.getBuiltins().number().getInteger();
}

@Specialization
Object doBigInteger(EnsoBigInteger value) {
Context ctx = Context.get(this);
return ctx.getBuiltins().number().getInteger();
}

@Specialization(guards = "isError(value)")
Object doError(Object value) {
return Context.get(this).getBuiltins().dataflowError();
}

boolean isError(Object value) {
return TypesGen.isDataflowError(value);
}

@Specialization(guards = "interop.hasMetaObject(value)")
Object doMetaObject(Object value, @CachedLibrary(limit = "3") InteropLibrary interop) {
try {
return interop.getMetaObject(value);
} catch (UnsupportedMessageException e) {
CompilerDirectives.transferToInterpreter();
Builtins builtins = Context.get(this).getBuiltins();
throw new PanicException(builtins.error().makeCompileError("invalid meta object"), this);
}
}

@Specialization(guards = "types.hasType(value)")
Object doType(Object value, @CachedLibrary(limit = "3") TypesLibrary types) {
return types.getType(value);
}

@Fallback
Object doAny(Object value) {
return Context.get(this).getBuiltins().any();
}
}
Loading

0 comments on commit c18bc0a

Please sign in to comment.