mirror of
https://github.com/enso-org/enso.git
synced 2024-12-18 22:01:51 +03:00
case of and intersection types (#11850)
Also: Prefer `getURI` path when gathering location of a source
This commit is contained in:
parent
e9b0ba95b0
commit
7998ed5218
@ -295,7 +295,7 @@ Error.should_equal self that frames_to_skip=0 =
|
|||||||
Number.should_equal : Float -> Float -> Integer -> Spec_Result
|
Number.should_equal : Float -> Float -> Integer -> Spec_Result
|
||||||
Number.should_equal self that epsilon=0 frames_to_skip=0 =
|
Number.should_equal self that epsilon=0 frames_to_skip=0 =
|
||||||
matches = case that of
|
matches = case that of
|
||||||
_ : Number -> self.equals that epsilon
|
n : Number -> self.equals n epsilon
|
||||||
_ -> self==that
|
_ -> self==that
|
||||||
case matches of
|
case matches of
|
||||||
True -> Spec_Result.Success
|
True -> Spec_Result.Success
|
||||||
|
@ -80,18 +80,14 @@ method calls will also only accept the value if it satisfies the type it
|
|||||||
_has been cast to_. Any additional remaining _hidden_ types
|
_has been cast to_. Any additional remaining _hidden_ types
|
||||||
can only be brought back through an _explicit_ cast.
|
can only be brought back through an _explicit_ cast.
|
||||||
To perform an explicit cast that can uncover the 'hidden' part of a type write
|
To perform an explicit cast that can uncover the 'hidden' part of a type write
|
||||||
`f = c:Float`.
|
`f = c:Float` or inspect the types in a `case` expression, e.g.
|
||||||
|
```ruby
|
||||||
<!--
|
case c of
|
||||||
When #11828 is fixed.
|
f : Float -> f.sqrt
|
||||||
|
_ -> "Not a float"
|
||||||
- or inspect the types in a `case` expression, e.g.
|
```
|
||||||
```
|
Remember to use `f.sqrt` and not `c.sqrt`. `f` in the case branch _has been cast to_ `Float` while
|
||||||
case c of
|
`c` in the case branch only _can be cast to_.
|
||||||
f : Float -> f.sqrt
|
|
||||||
_ -> "Not a float"
|
|
||||||
```
|
|
||||||
-->
|
|
||||||
|
|
||||||
> [!WARNING]
|
> [!WARNING]
|
||||||
> Keep in mind that while both argument type check in method definitions and a
|
> Keep in mind that while both argument type check in method definitions and a
|
||||||
|
@ -220,7 +220,7 @@ final class ExecutionCallbacks implements IdExecutionService.Callbacks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var typeOfNode = TypeOfNode.getUncached();
|
var typeOfNode = TypeOfNode.getUncached();
|
||||||
Type[] allTypes = value == null ? null : typeOfNode.findAllTypesOrNull(value);
|
Type[] allTypes = value == null ? null : typeOfNode.findAllTypesOrNull(value, true);
|
||||||
if (allTypes != null) {
|
if (allTypes != null) {
|
||||||
String[] result = new String[allTypes.length];
|
String[] result = new String[allTypes.length];
|
||||||
for (var i = 0; i < allTypes.length; i++) {
|
for (var i = 0; i < allTypes.length; i++) {
|
||||||
|
@ -49,8 +49,9 @@ public class TypeOfNodeMultiValueTest {
|
|||||||
new TestRootNode(
|
new TestRootNode(
|
||||||
(frame) -> {
|
(frame) -> {
|
||||||
var arg = frame.getArguments()[0];
|
var arg = frame.getArguments()[0];
|
||||||
|
var allTypes = (boolean) frame.getArguments()[1];
|
||||||
var t = node.findTypeOrError(arg);
|
var t = node.findTypeOrError(arg);
|
||||||
var all = node.findAllTypesOrNull(arg);
|
var all = node.findAllTypesOrNull(arg, allTypes);
|
||||||
return new Object[] {t, all};
|
return new Object[] {t, all};
|
||||||
});
|
});
|
||||||
root.insertChildren(node);
|
root.insertChildren(node);
|
||||||
@ -116,16 +117,22 @@ public class TypeOfNodeMultiValueTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void typeOfCheck() {
|
public void typeOfCheckAllTypes() {
|
||||||
assertType(value, type, typeIndex);
|
assertType(value, type, typeIndex, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void assertType(Object value, String expectedTypeName, int typeIndex) {
|
@Test
|
||||||
|
public void typeOfCheckHasBeenCastToTypes() {
|
||||||
|
assertType(value, type, typeIndex, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertType(
|
||||||
|
Object value, String expectedTypeName, int typeIndex, boolean allTypes) {
|
||||||
assertNotNull("Value " + value + " should have a type", expectedTypeName);
|
assertNotNull("Value " + value + " should have a type", expectedTypeName);
|
||||||
ContextUtils.executeInContext(
|
ContextUtils.executeInContext(
|
||||||
ctx(),
|
ctx(),
|
||||||
() -> {
|
() -> {
|
||||||
var pairResult = (Object[]) testTypesCall.call(value);
|
var pairResult = (Object[]) testTypesCall.call(value, allTypes);
|
||||||
var t = pairResult[0];
|
var t = pairResult[0];
|
||||||
var all = (Object[]) pairResult[1];
|
var all = (Object[]) pairResult[1];
|
||||||
|
|
||||||
|
@ -3,8 +3,10 @@ package org.enso.interpreter.node.expression.builtin.meta;
|
|||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
import com.oracle.truffle.api.RootCallTarget;
|
import com.oracle.truffle.api.RootCallTarget;
|
||||||
|
import org.enso.interpreter.runtime.EnsoContext;
|
||||||
import org.enso.interpreter.runtime.callable.UnresolvedConstructor;
|
import org.enso.interpreter.runtime.callable.UnresolvedConstructor;
|
||||||
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
|
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
|
||||||
|
import org.enso.interpreter.runtime.data.EnsoMultiValue;
|
||||||
import org.enso.interpreter.runtime.data.Type;
|
import org.enso.interpreter.runtime.data.Type;
|
||||||
import org.enso.interpreter.runtime.library.dispatch.TypeOfNode;
|
import org.enso.interpreter.runtime.library.dispatch.TypeOfNode;
|
||||||
import org.enso.test.utils.ContextUtils;
|
import org.enso.test.utils.ContextUtils;
|
||||||
@ -28,8 +30,9 @@ public class TypeOfNodeValueTest {
|
|||||||
new TestRootNode(
|
new TestRootNode(
|
||||||
(frame) -> {
|
(frame) -> {
|
||||||
var arg = frame.getArguments()[0];
|
var arg = frame.getArguments()[0];
|
||||||
|
var allTypes = (boolean) frame.getArguments()[1];
|
||||||
var t = node.findTypeOrError(arg);
|
var t = node.findTypeOrError(arg);
|
||||||
var all = node.findAllTypesOrNull(arg);
|
var all = node.findAllTypesOrNull(arg, allTypes);
|
||||||
return new Object[] {t, all};
|
return new Object[] {t, all};
|
||||||
});
|
});
|
||||||
root.insertChildren(node);
|
root.insertChildren(node);
|
||||||
@ -54,7 +57,7 @@ public class TypeOfNodeValueTest {
|
|||||||
ctx(),
|
ctx(),
|
||||||
() -> {
|
() -> {
|
||||||
var cnstr = UnresolvedConstructor.build(null, "Unknown_Name");
|
var cnstr = UnresolvedConstructor.build(null, "Unknown_Name");
|
||||||
var arr = (Object[]) testTypesCall.call(cnstr);
|
var arr = (Object[]) testTypesCall.call(cnstr, true);
|
||||||
var type = (Type) arr[0];
|
var type = (Type) arr[0];
|
||||||
var allTypes = (Type[]) arr[1];
|
var allTypes = (Type[]) arr[1];
|
||||||
assertEquals("Function", type.getName());
|
assertEquals("Function", type.getName());
|
||||||
@ -70,7 +73,7 @@ public class TypeOfNodeValueTest {
|
|||||||
ctx(),
|
ctx(),
|
||||||
() -> {
|
() -> {
|
||||||
var cnstr = UnresolvedSymbol.build("Unknown_Name", null);
|
var cnstr = UnresolvedSymbol.build("Unknown_Name", null);
|
||||||
var arr = (Object[]) testTypesCall.call(cnstr);
|
var arr = (Object[]) testTypesCall.call(cnstr, true);
|
||||||
var type = (Type) arr[0];
|
var type = (Type) arr[0];
|
||||||
var allTypes = (Type[]) arr[1];
|
var allTypes = (Type[]) arr[1];
|
||||||
assertEquals("Function", type.getName());
|
assertEquals("Function", type.getName());
|
||||||
@ -79,4 +82,29 @@ public class TypeOfNodeValueTest {
|
|||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void multiValueWithHiddenType() {
|
||||||
|
ContextUtils.executeInContext(
|
||||||
|
ctx(),
|
||||||
|
() -> {
|
||||||
|
var ensoCtx = EnsoContext.get(testTypesCall.getRootNode());
|
||||||
|
var types =
|
||||||
|
new Type[] {
|
||||||
|
ensoCtx.getBuiltins().number().getInteger(), ensoCtx.getBuiltins().text()
|
||||||
|
};
|
||||||
|
var multi = EnsoMultiValue.create(types, 1, new Object[] {42L, "Meaning"});
|
||||||
|
var arr = (Object[]) testTypesCall.call(multi, true);
|
||||||
|
var allTypes = (Type[]) arr[1];
|
||||||
|
assertEquals("Two types", 2, allTypes.length);
|
||||||
|
assertEquals("Integer", types[0], allTypes[0]);
|
||||||
|
assertEquals("Text", types[1], allTypes[1]);
|
||||||
|
|
||||||
|
var arr1 = (Object[]) testTypesCall.call(multi, false);
|
||||||
|
var allTypes1 = (Type[]) arr1[1];
|
||||||
|
assertEquals("Just one type", 1, allTypes1.length);
|
||||||
|
assertEquals("Integer", types[0], allTypes1[0]);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -152,20 +152,13 @@ final class BinaryOperatorNode extends ExpressionNode {
|
|||||||
InvokeFunctionNode invokeNode) {
|
InvokeFunctionNode invokeNode) {
|
||||||
var selfType = findType(typeOfNode, self);
|
var selfType = findType(typeOfNode, self);
|
||||||
if (that instanceof EnsoMultiValue multi) {
|
if (that instanceof EnsoMultiValue multi) {
|
||||||
var all = typeOfNode.findAllTypesOrNull(multi);
|
var all = typeOfNode.findAllTypesOrNull(multi, false);
|
||||||
assert all != null;
|
assert all != null;
|
||||||
for (var thatType : all) {
|
for (var thatType : all) {
|
||||||
var fn = findSymbol(symbol, thatType);
|
var fn = findSymbol(symbol, thatType);
|
||||||
if (fn != null) {
|
if (fn != null) {
|
||||||
var thatCasted =
|
|
||||||
EnsoMultiValue.CastToNode.getUncached()
|
|
||||||
.findTypeOrNull(thatType, multi, false, false);
|
|
||||||
if (thatCasted == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
var result =
|
var result =
|
||||||
doDispatch(
|
doDispatch(frame, self, multi, selfType, thatType, fn, convertNode, invokeNode);
|
||||||
frame, self, thatCasted, selfType, thatType, fn, convertNode, invokeNode);
|
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import com.oracle.truffle.api.frame.VirtualFrame;
|
|||||||
import com.oracle.truffle.api.nodes.NodeInfo;
|
import com.oracle.truffle.api.nodes.NodeInfo;
|
||||||
import com.oracle.truffle.api.profiles.CountingConditionProfile;
|
import com.oracle.truffle.api.profiles.CountingConditionProfile;
|
||||||
import org.enso.interpreter.node.expression.builtin.meta.IsValueOfTypeNode;
|
import org.enso.interpreter.node.expression.builtin.meta.IsValueOfTypeNode;
|
||||||
|
import org.enso.interpreter.runtime.data.EnsoMultiValue;
|
||||||
import org.enso.interpreter.runtime.data.Type;
|
import org.enso.interpreter.runtime.data.Type;
|
||||||
|
|
||||||
/** An implementation of the case expression specialised to working on types. */
|
/** An implementation of the case expression specialised to working on types. */
|
||||||
@ -33,7 +34,13 @@ public class CatchTypeBranchNode extends BranchNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void execute(VirtualFrame frame, Object state, Object value) {
|
public void execute(VirtualFrame frame, Object state, Object value) {
|
||||||
if (profile.profile(isValueOfTypeNode.execute(expectedType, value))) {
|
if (profile.profile(isValueOfTypeNode.execute(expectedType, value, true))) {
|
||||||
|
if (value instanceof EnsoMultiValue multi) {
|
||||||
|
var replacement =
|
||||||
|
EnsoMultiValue.CastToNode.getUncached().findTypeOrNull(expectedType, multi, true, true);
|
||||||
|
assert replacement != null : "Must find the type, when isValueOfTypeNode is true";
|
||||||
|
value = replacement;
|
||||||
|
}
|
||||||
accept(frame, state, new Object[] {value});
|
accept(frame, state, new Object[] {value});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ public abstract class CatchPanicNode extends Node {
|
|||||||
AbstractTruffleException originalException,
|
AbstractTruffleException originalException,
|
||||||
InteropLibrary interopLibrary) {
|
InteropLibrary interopLibrary) {
|
||||||
|
|
||||||
if (profile.profile(isValueOfTypeNode.execute(panicType, payload))) {
|
if (profile.profile(isValueOfTypeNode.execute(panicType, payload, true))) {
|
||||||
var builtins = EnsoContext.get(this).getBuiltins();
|
var builtins = EnsoContext.get(this).getBuiltins();
|
||||||
var cons = builtins.caughtPanic().getUniqueConstructor();
|
var cons = builtins.caughtPanic().getUniqueConstructor();
|
||||||
var caughtPanic =
|
var caughtPanic =
|
||||||
|
@ -4,6 +4,7 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
|||||||
import com.oracle.truffle.api.TruffleStackTrace;
|
import com.oracle.truffle.api.TruffleStackTrace;
|
||||||
import com.oracle.truffle.api.TruffleStackTraceElement;
|
import com.oracle.truffle.api.TruffleStackTraceElement;
|
||||||
import com.oracle.truffle.api.nodes.Node;
|
import com.oracle.truffle.api.nodes.Node;
|
||||||
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import org.enso.interpreter.dsl.BuiltinMethod;
|
import org.enso.interpreter.dsl.BuiltinMethod;
|
||||||
@ -76,6 +77,11 @@ public class GetStackTraceTextNode extends Node {
|
|||||||
var sourceLoc = errorFrame.getLocation().getEncapsulatingSourceSection();
|
var sourceLoc = errorFrame.getLocation().getEncapsulatingSourceSection();
|
||||||
if (sourceLoc != null) {
|
if (sourceLoc != null) {
|
||||||
var path = sourceLoc.getSource().getPath();
|
var path = sourceLoc.getSource().getPath();
|
||||||
|
try {
|
||||||
|
path = new File(sourceLoc.getSource().getURI()).getPath();
|
||||||
|
} catch (IllegalArgumentException | NullPointerException ignore) {
|
||||||
|
// keep original value of path
|
||||||
|
}
|
||||||
var ident = (path != null) ? path : sourceLoc.getSource().getName();
|
var ident = (path != null) ? path : sourceLoc.getSource().getName();
|
||||||
var loc =
|
var loc =
|
||||||
(sourceLoc.getStartLine() == sourceLoc.getEndLine())
|
(sourceLoc.getStartLine() == sourceLoc.getEndLine())
|
||||||
|
@ -331,7 +331,7 @@ abstract class EqualsSimpleNode extends Node {
|
|||||||
@Shared("multiCast") @Cached EnsoMultiValue.CastToNode castNode,
|
@Shared("multiCast") @Cached EnsoMultiValue.CastToNode castNode,
|
||||||
@Shared("multiType") @Cached TypeOfNode typesNode,
|
@Shared("multiType") @Cached TypeOfNode typesNode,
|
||||||
@Shared("multiEquals") @Cached EqualsSimpleNode delegate) {
|
@Shared("multiEquals") @Cached EqualsSimpleNode delegate) {
|
||||||
var types = typesNode.findAllTypesOrNull(self);
|
var types = typesNode.findAllTypesOrNull(self, false);
|
||||||
assert types != null;
|
assert types != null;
|
||||||
for (var t : types) {
|
for (var t : types) {
|
||||||
var value = castNode.findTypeOrNull(t, self, false, false);
|
var value = castNode.findTypeOrNull(t, self, false, false);
|
||||||
|
@ -13,6 +13,6 @@ public class IsANode extends Node {
|
|||||||
private @Child IsValueOfTypeNode check = IsValueOfTypeNode.build();
|
private @Child IsValueOfTypeNode check = IsValueOfTypeNode.build();
|
||||||
|
|
||||||
public boolean execute(@AcceptsError Object value, Object type) {
|
public boolean execute(@AcceptsError Object value, Object type) {
|
||||||
return check.execute(type, value);
|
return check.execute(type, value, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,53 +17,72 @@ import org.enso.interpreter.runtime.data.Type;
|
|||||||
import org.enso.interpreter.runtime.library.dispatch.TypeOfNode;
|
import org.enso.interpreter.runtime.library.dispatch.TypeOfNode;
|
||||||
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
|
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
|
||||||
import org.enso.interpreter.runtime.number.EnsoBigInteger;
|
import org.enso.interpreter.runtime.number.EnsoBigInteger;
|
||||||
import org.enso.interpreter.runtime.type.TypesGen;
|
|
||||||
|
|
||||||
/** An implementation of the payload check against the expected panic type. */
|
/** An implementation of the payload check against the expected panic type. */
|
||||||
@NodeInfo(shortName = "IsValueOfTypeNode")
|
@NodeInfo(shortName = "IsValueOfTypeNode")
|
||||||
public abstract class IsValueOfTypeNode extends Node {
|
public abstract class IsValueOfTypeNode extends Node {
|
||||||
|
IsValueOfTypeNode() {}
|
||||||
|
|
||||||
public static IsValueOfTypeNode build() {
|
public static IsValueOfTypeNode build() {
|
||||||
return IsValueOfTypeNodeGen.create();
|
return IsValueOfTypeNodeGen.create();
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract boolean execute(Object expectedType, Object payload);
|
/**
|
||||||
|
* @param expectedType the type to check
|
||||||
|
* @param obj the object to check
|
||||||
|
* @param includeExtraTypes specify {@code false} to return only <em>types value has already been
|
||||||
|
* cast to</em>, specify {@code true} to return all <em>types value can be cast to</em>
|
||||||
|
*/
|
||||||
|
public abstract boolean execute(Object expectedType, Object obj, boolean includeExtraTypes);
|
||||||
|
|
||||||
@Specialization(guards = {"types.hasType(payload)"})
|
@Specialization(guards = {"types.hasType(payload)"})
|
||||||
boolean doTyped(
|
boolean doTyped(
|
||||||
Object expectedType,
|
Object expectedType,
|
||||||
Object payload,
|
Object payload,
|
||||||
|
boolean allTypes,
|
||||||
@Shared("types") @CachedLibrary(limit = "3") TypesLibrary types,
|
@Shared("types") @CachedLibrary(limit = "3") TypesLibrary types,
|
||||||
@Cached Typed typed) {
|
@Cached Typed typed) {
|
||||||
return typed.execute(expectedType, payload);
|
return typed.execute(expectedType, payload, allTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Specialization(guards = {"!types.hasType(payload)"})
|
@Specialization(guards = {"!types.hasType(payload)"})
|
||||||
boolean doPolyglot(
|
boolean doPolyglot(
|
||||||
Object expectedType,
|
Object expectedType,
|
||||||
Object payload,
|
Object payload,
|
||||||
|
boolean allTypes,
|
||||||
@Shared("types") @CachedLibrary(limit = "3") TypesLibrary types,
|
@Shared("types") @CachedLibrary(limit = "3") TypesLibrary types,
|
||||||
@Cached Untyped untyped) {
|
@Cached Untyped untyped) {
|
||||||
return untyped.execute(expectedType, payload);
|
return untyped.execute(expectedType, payload, allTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean typeAndCheck(
|
private static boolean typeAndCheck(
|
||||||
Object payload,
|
Object payload,
|
||||||
Object expectedType,
|
Object expectedMeta,
|
||||||
|
boolean allTypes,
|
||||||
TypeOfNode typeOfNode,
|
TypeOfNode typeOfNode,
|
||||||
IsSameObjectNode isSameObject,
|
IsSameObjectNode isSameObject,
|
||||||
CountingConditionProfile isSameObjectProfile) {
|
CountingConditionProfile isSameObjectProfile) {
|
||||||
Object tpeOfPayload = typeOfNode.findTypeOrError(payload);
|
if (expectedMeta instanceof Type expectedType) {
|
||||||
if (isSameObjectProfile.profile(isSameObject.execute(expectedType, tpeOfPayload))) {
|
var arr = typeOfNode.findAllTypesOrNull(payload, allTypes);
|
||||||
return true;
|
if (arr == null) {
|
||||||
} else if (TypesGen.isType(tpeOfPayload)) {
|
return false;
|
||||||
Type tpe = TypesGen.asType(tpeOfPayload);
|
}
|
||||||
var ctx = EnsoContext.get(typeOfNode);
|
for (var tpeOfPayload : arr) {
|
||||||
for (var superTpe : tpe.allTypes(ctx)) {
|
if (isSameObjectProfile.profile(isSameObject.execute(expectedType, tpeOfPayload))) {
|
||||||
boolean testSuperTpe = isSameObject.execute(expectedType, superTpe);
|
|
||||||
if (testSuperTpe) {
|
|
||||||
return true;
|
return true;
|
||||||
|
} else {
|
||||||
|
var ctx = EnsoContext.get(typeOfNode);
|
||||||
|
for (var superTpe : tpeOfPayload.allTypes(ctx)) {
|
||||||
|
boolean testSuperTpe = isSameObject.execute(expectedType, superTpe);
|
||||||
|
if (testSuperTpe) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
var tpe = typeOfNode.findTypeOrError(payload);
|
||||||
|
return isSameObject.execute(expectedMeta, tpe);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -73,33 +92,33 @@ public abstract class IsValueOfTypeNode extends Node {
|
|||||||
private @Child TypeOfNode typeOfNode = TypeOfNode.create();
|
private @Child TypeOfNode typeOfNode = TypeOfNode.create();
|
||||||
private final CountingConditionProfile profile = CountingConditionProfile.create();
|
private final CountingConditionProfile profile = CountingConditionProfile.create();
|
||||||
|
|
||||||
abstract boolean execute(Object expectedType, Object payload);
|
abstract boolean execute(Object expectedType, Object payload, boolean allTypes);
|
||||||
|
|
||||||
@Specialization(guards = "isAnyType(expectedType)")
|
@Specialization(guards = "isAnyType(expectedType)")
|
||||||
boolean doAnyType(Object expectedType, Object payload) {
|
boolean doAnyType(Object expectedType, Object payload, boolean allTypes) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Specialization
|
@Specialization
|
||||||
boolean doLongCheck(Type expectedType, long payload) {
|
boolean doLongCheck(Type expectedType, long payload, boolean allTypes) {
|
||||||
var numbers = EnsoContext.get(this).getBuiltins().number();
|
var numbers = EnsoContext.get(this).getBuiltins().number();
|
||||||
return checkParentTypes(numbers.getInteger(), expectedType);
|
return checkParentTypes(numbers.getInteger(), expectedType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Specialization
|
@Specialization
|
||||||
boolean doDoubleCheck(Type expectedType, double payload) {
|
boolean doDoubleCheck(Type expectedType, double payload, boolean allTypes) {
|
||||||
var numbers = EnsoContext.get(this).getBuiltins().number();
|
var numbers = EnsoContext.get(this).getBuiltins().number();
|
||||||
return checkParentTypes(numbers.getFloat(), expectedType);
|
return checkParentTypes(numbers.getFloat(), expectedType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Specialization
|
@Specialization
|
||||||
boolean doBigIntegerCheck(Type expectedType, EnsoBigInteger value) {
|
boolean doBigIntegerCheck(Type expectedType, EnsoBigInteger value, boolean allTypes) {
|
||||||
var numbers = EnsoContext.get(this).getBuiltins().number();
|
var numbers = EnsoContext.get(this).getBuiltins().number();
|
||||||
return checkParentTypes(numbers.getInteger(), expectedType);
|
return checkParentTypes(numbers.getInteger(), expectedType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Specialization
|
@Specialization
|
||||||
boolean doUnresolvedSymbol(Type expectedType, UnresolvedSymbol value) {
|
boolean doUnresolvedSymbol(Type expectedType, UnresolvedSymbol value, boolean allTypes) {
|
||||||
var funTpe = EnsoContext.get(this).getBuiltins().function();
|
var funTpe = EnsoContext.get(this).getBuiltins().function();
|
||||||
return expectedType == funTpe;
|
return expectedType == funTpe;
|
||||||
}
|
}
|
||||||
@ -119,8 +138,9 @@ public abstract class IsValueOfTypeNode extends Node {
|
|||||||
boolean doType(
|
boolean doType(
|
||||||
Type expectedType,
|
Type expectedType,
|
||||||
Object payload,
|
Object payload,
|
||||||
|
boolean allTypes,
|
||||||
@Shared("types") @CachedLibrary(limit = "3") TypesLibrary types) {
|
@Shared("types") @CachedLibrary(limit = "3") TypesLibrary types) {
|
||||||
return typeAndCheck(payload, expectedType, typeOfNode, isSameObject, profile);
|
return typeAndCheck(payload, expectedType, allTypes, typeOfNode, isSameObject, profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Specialization(
|
@Specialization(
|
||||||
@ -131,13 +151,14 @@ public abstract class IsValueOfTypeNode extends Node {
|
|||||||
public boolean doArrayViaType(
|
public boolean doArrayViaType(
|
||||||
Object expectedType,
|
Object expectedType,
|
||||||
Object payload,
|
Object payload,
|
||||||
|
boolean allTypes,
|
||||||
@CachedLibrary(limit = "3") InteropLibrary interop,
|
@CachedLibrary(limit = "3") InteropLibrary interop,
|
||||||
@Shared("types") @CachedLibrary(limit = "3") TypesLibrary types) {
|
@Shared("types") @CachedLibrary(limit = "3") TypesLibrary types) {
|
||||||
return EnsoContext.get(this).getBuiltins().array() == types.getType(payload);
|
return EnsoContext.get(this).getBuiltins().array() == types.getType(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Fallback
|
@Fallback
|
||||||
boolean doOther(Object expectedType, Object payload) {
|
boolean doOther(Object expectedType, Object payload, boolean allTypes) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,7 +176,7 @@ public abstract class IsValueOfTypeNode extends Node {
|
|||||||
private @Child TypeOfNode typeOfNode = TypeOfNode.create();
|
private @Child TypeOfNode typeOfNode = TypeOfNode.create();
|
||||||
private final CountingConditionProfile profile = CountingConditionProfile.create();
|
private final CountingConditionProfile profile = CountingConditionProfile.create();
|
||||||
|
|
||||||
abstract boolean execute(Object expectedType, Object payload);
|
abstract boolean execute(Object expectedType, Object payload, boolean allTypes);
|
||||||
|
|
||||||
@Specialization(
|
@Specialization(
|
||||||
guards = {
|
guards = {
|
||||||
@ -163,7 +184,10 @@ public abstract class IsValueOfTypeNode extends Node {
|
|||||||
"isMetaInstance(interop, expectedType, payload)"
|
"isMetaInstance(interop, expectedType, payload)"
|
||||||
})
|
})
|
||||||
boolean doPolyglotType(
|
boolean doPolyglotType(
|
||||||
Object expectedType, Object payload, @CachedLibrary(limit = "3") InteropLibrary interop) {
|
Object expectedType,
|
||||||
|
Object payload,
|
||||||
|
boolean allTypes,
|
||||||
|
@CachedLibrary(limit = "3") InteropLibrary interop) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,8 +200,8 @@ public abstract class IsValueOfTypeNode extends Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Fallback
|
@Fallback
|
||||||
public boolean doOther(Object expectedType, Object payload) {
|
public boolean doOther(Object expectedType, Object payload, boolean allTypes) {
|
||||||
return typeAndCheck(payload, expectedType, typeOfNode, isSameObject, profile);
|
return typeAndCheck(payload, expectedType, allTypes, typeOfNode, isSameObject, profile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ non-sealed abstract class MetaTypeCheckNode extends AbstractTypeCheckNode {
|
|||||||
if (isAllFitValue(v)) {
|
if (isAllFitValue(v)) {
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
if (isA.execute(expectedSupplier.get(), v)) {
|
if (isA.execute(expectedSupplier.get(), v, true)) {
|
||||||
return v;
|
return v;
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
|
@ -123,7 +123,7 @@ non-sealed abstract class SingleTypeCheckNode extends AbstractTypeCheckNode {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (checkType.execute(expectedType, v)) {
|
if (checkType.execute(expectedType, v, isAllTypes())) {
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@ -176,7 +176,7 @@ non-sealed abstract class SingleTypeCheckNode extends AbstractTypeCheckNode {
|
|||||||
|
|
||||||
Type[] findType(TypeOfNode typeOfNode, Object v, Type[] previous) {
|
Type[] findType(TypeOfNode typeOfNode, Object v, Type[] previous) {
|
||||||
if (v instanceof EnsoMultiValue multi) {
|
if (v instanceof EnsoMultiValue multi) {
|
||||||
var all = typeOfNode.findAllTypesOrNull(multi);
|
var all = typeOfNode.findAllTypesOrNull(multi, isAllTypes());
|
||||||
assert all != null;
|
assert all != null;
|
||||||
var to = 0;
|
var to = 0;
|
||||||
// only consider methodDispatchTypes and not "all types" of the multi value
|
// only consider methodDispatchTypes and not "all types" of the multi value
|
||||||
|
@ -88,8 +88,12 @@ public final class EnsoMultiValue extends EnsoObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ExportMessage
|
@ExportMessage
|
||||||
final Type[] allTypes() {
|
final Type[] allTypes(boolean includeExtraTypes) {
|
||||||
return types.clone();
|
if (includeExtraTypes || methodDispatchTypes == types.length) {
|
||||||
|
return types.clone();
|
||||||
|
} else {
|
||||||
|
return Arrays.copyOf(types, methodDispatchTypes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ExportMessage
|
@ExportMessage
|
||||||
@ -452,19 +456,22 @@ public final class EnsoMultiValue extends EnsoObject {
|
|||||||
|
|
||||||
@Specialization
|
@Specialization
|
||||||
Object castsToAType(Type type, EnsoMultiValue mv, boolean reorderOnly, boolean allTypes) {
|
Object castsToAType(Type type, EnsoMultiValue mv, boolean reorderOnly, boolean allTypes) {
|
||||||
|
var ctx = EnsoContext.get(this);
|
||||||
var max = allTypes ? mv.types.length : mv.methodDispatchTypes;
|
var max = allTypes ? mv.types.length : mv.methodDispatchTypes;
|
||||||
for (var i = 0; i < max; i++) {
|
for (var i = 0; i < max; i++) {
|
||||||
if (mv.types[i] == type) {
|
for (var t : mv.types[i].allTypes(ctx)) {
|
||||||
if (reorderOnly) {
|
if (t == type) {
|
||||||
var copyTypes = mv.types.clone();
|
if (reorderOnly) {
|
||||||
var copyValues = mv.values.clone();
|
var copyTypes = mv.types.clone();
|
||||||
copyTypes[i] = mv.types[0];
|
var copyValues = mv.values.clone();
|
||||||
copyValues[i] = mv.values[0];
|
copyTypes[i] = mv.types[0];
|
||||||
copyTypes[0] = mv.types[i];
|
copyValues[i] = mv.values[0];
|
||||||
copyValues[0] = mv.values[i];
|
copyTypes[0] = mv.types[i];
|
||||||
return EnsoMultiValue.create(copyTypes, 1, copyValues);
|
copyValues[0] = mv.values[i];
|
||||||
} else {
|
return EnsoMultiValue.create(copyTypes, 1, copyValues);
|
||||||
return mv.values[i];
|
} else {
|
||||||
|
return mv.values[i];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@ public abstract class TypeOfNode extends Node {
|
|||||||
*/
|
*/
|
||||||
public final TruffleObject findTypeOrError(Object value) {
|
public final TruffleObject findTypeOrError(Object value) {
|
||||||
try {
|
try {
|
||||||
var types = executeTypes(value);
|
var types = executeTypes(value, false);
|
||||||
return types[0];
|
return types[0];
|
||||||
} catch (NonTypeResult plain) {
|
} catch (NonTypeResult plain) {
|
||||||
return plain.result;
|
return plain.result;
|
||||||
@ -76,11 +76,13 @@ public abstract class TypeOfNode extends Node {
|
|||||||
* that the returned array is going to have at least one element, if it is non-{@code null}.
|
* that the returned array is going to have at least one element, if it is non-{@code null}.
|
||||||
*
|
*
|
||||||
* @param value the value to check
|
* @param value the value to check
|
||||||
|
* @param includeExtraTypes specify {@code false} to return only <em>types value has already been
|
||||||
|
* case to</em>, specify {@code true} to return all <em>types value can be cast to</em>
|
||||||
* @return either types associated with the value or {@code null} if there is no such type
|
* @return either types associated with the value or {@code null} if there is no such type
|
||||||
*/
|
*/
|
||||||
public final Type[] findAllTypesOrNull(Object value) {
|
public final Type[] findAllTypesOrNull(Object value, boolean includeExtraTypes) {
|
||||||
try {
|
try {
|
||||||
var types = executeTypes(value);
|
var types = executeTypes(value, includeExtraTypes);
|
||||||
assert types != null && types.length > 0;
|
assert types != null && types.length > 0;
|
||||||
return types;
|
return types;
|
||||||
} catch (NonTypeResult ex) {
|
} catch (NonTypeResult ex) {
|
||||||
@ -92,11 +94,13 @@ public abstract class TypeOfNode extends Node {
|
|||||||
* Internal implementation call to delegate to various {@link Specialization} methods.
|
* Internal implementation call to delegate to various {@link Specialization} methods.
|
||||||
*
|
*
|
||||||
* @param value the value to find type for
|
* @param value the value to find type for
|
||||||
|
* @param includeExtraTypes {@code false} to return only <em>types value has already been case
|
||||||
|
* to</em>, {@code true} to return all <em>types value can be cast to</em>
|
||||||
* @return array of types with at least one element, but possibly more
|
* @return array of types with at least one element, but possibly more
|
||||||
* @throws NonTypeResult when there is a <em>interop value</em> result, but it is not a {@link
|
* @throws NonTypeResult when there is a <em>interop value</em> result, but it is not a {@link
|
||||||
* Type}
|
* Type}
|
||||||
*/
|
*/
|
||||||
abstract Type[] executeTypes(Object value) throws NonTypeResult;
|
abstract Type[] executeTypes(Object value, boolean includeExtraTypes) throws NonTypeResult;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates new optimizing instance of this node.
|
* Creates new optimizing instance of this node.
|
||||||
@ -123,38 +127,39 @@ public abstract class TypeOfNode extends Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Specialization
|
@Specialization
|
||||||
Type[] doUnresolvedSymbol(UnresolvedSymbol value) {
|
Type[] doUnresolvedSymbol(UnresolvedSymbol value, boolean allTypes) {
|
||||||
return fromType(EnsoContext.get(this).getBuiltins().function());
|
return fromType(EnsoContext.get(this).getBuiltins().function());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Specialization
|
@Specialization
|
||||||
Type[] doDouble(double value) {
|
Type[] doDouble(double value, boolean allTypes) {
|
||||||
return fromType(EnsoContext.get(this).getBuiltins().number().getFloat());
|
return fromType(EnsoContext.get(this).getBuiltins().number().getFloat());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Specialization
|
@Specialization
|
||||||
Type[] doLong(long value) {
|
Type[] doLong(long value, boolean allTypes) {
|
||||||
return fromType(EnsoContext.get(this).getBuiltins().number().getInteger());
|
return fromType(EnsoContext.get(this).getBuiltins().number().getInteger());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Specialization
|
@Specialization
|
||||||
Type[] doBigInteger(EnsoBigInteger value) {
|
Type[] doBigInteger(EnsoBigInteger value, boolean allTypes) {
|
||||||
return fromType(EnsoContext.get(this).getBuiltins().number().getInteger());
|
return fromType(EnsoContext.get(this).getBuiltins().number().getInteger());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Specialization
|
@Specialization
|
||||||
Type[] doPanicException(PanicException value) {
|
Type[] doPanicException(PanicException value, boolean allTypes) {
|
||||||
return fromType(EnsoContext.get(this).getBuiltins().panic());
|
return fromType(EnsoContext.get(this).getBuiltins().panic());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Specialization
|
@Specialization
|
||||||
Type[] doPanicSentinel(PanicSentinel value) {
|
Type[] doPanicSentinel(PanicSentinel value, boolean allTypes) {
|
||||||
return fromType(EnsoContext.get(this).getBuiltins().panic());
|
return fromType(EnsoContext.get(this).getBuiltins().panic());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Specialization
|
@Specialization
|
||||||
Type[] doWarning(WithWarnings value, @Cached TypeOfNode withoutWarning) throws NonTypeResult {
|
Type[] doWarning(WithWarnings value, boolean includeExtraTypes, @Cached TypeOfNode withoutWarning)
|
||||||
return withoutWarning.executeTypes(value.getValue());
|
throws NonTypeResult {
|
||||||
|
return withoutWarning.executeTypes(value.getValue(), includeExtraTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean isWithType(Object value, TypesLibrary types, InteropLibrary iop) {
|
static boolean isWithType(Object value, TypesLibrary types, InteropLibrary iop) {
|
||||||
@ -180,6 +185,7 @@ public abstract class TypeOfNode extends Node {
|
|||||||
@Specialization(guards = {"isWithoutType(value, types)"})
|
@Specialization(guards = {"isWithoutType(value, types)"})
|
||||||
Type[] withoutType(
|
Type[] withoutType(
|
||||||
Object value,
|
Object value,
|
||||||
|
boolean allTypes,
|
||||||
@Shared("interop") @CachedLibrary(limit = "3") InteropLibrary interop,
|
@Shared("interop") @CachedLibrary(limit = "3") InteropLibrary interop,
|
||||||
@Shared("types") @CachedLibrary(limit = "3") TypesLibrary types,
|
@Shared("types") @CachedLibrary(limit = "3") TypesLibrary types,
|
||||||
@Cached WithoutType delegate)
|
@Cached WithoutType delegate)
|
||||||
@ -201,14 +207,15 @@ public abstract class TypeOfNode extends Node {
|
|||||||
@Specialization(guards = {"isWithType(value, types, interop)"})
|
@Specialization(guards = {"isWithType(value, types, interop)"})
|
||||||
Type[] doType(
|
Type[] doType(
|
||||||
Object value,
|
Object value,
|
||||||
|
boolean includeExtraTypes,
|
||||||
@Shared("interop") @CachedLibrary(limit = "3") InteropLibrary interop,
|
@Shared("interop") @CachedLibrary(limit = "3") InteropLibrary interop,
|
||||||
@Shared("types") @CachedLibrary(limit = "3") TypesLibrary types) {
|
@Shared("types") @CachedLibrary(limit = "3") TypesLibrary types) {
|
||||||
return types.allTypes(value);
|
return types.allTypes(value, includeExtraTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Fallback
|
@Fallback
|
||||||
@CompilerDirectives.TruffleBoundary
|
@CompilerDirectives.TruffleBoundary
|
||||||
Type[] doAny(Object value) throws NonTypeResult {
|
Type[] doAny(Object value, boolean allTypes) throws NonTypeResult {
|
||||||
var err =
|
var err =
|
||||||
DataflowError.withDefaultTrace(
|
DataflowError.withDefaultTrace(
|
||||||
EnsoContext.get(this)
|
EnsoContext.get(this)
|
||||||
|
@ -74,9 +74,11 @@ public abstract class TypesLibrary extends Library {
|
|||||||
* #getType(java.lang.Object)} and returns array with one element of that type
|
* #getType(java.lang.Object)} and returns array with one element of that type
|
||||||
*
|
*
|
||||||
* @param receiver the typed object
|
* @param receiver the typed object
|
||||||
|
* @param includeExtraTypes specify {@code false} to return only <em>types value has already been
|
||||||
|
* case to</em>, specify {@code true} to return all <em>types value can be cast to</em>
|
||||||
* @return the corresponding types for the {@code receiver}
|
* @return the corresponding types for the {@code receiver}
|
||||||
*/
|
*/
|
||||||
public Type[] allTypes(Object receiver) {
|
public Type[] allTypes(Object receiver, boolean includeExtraTypes) {
|
||||||
var t = getType(receiver);
|
var t = getType(receiver);
|
||||||
assert t != null : "null type for " + receiver;
|
assert t != null : "null type for " + receiver;
|
||||||
return new Type[] {t};
|
return new Type[] {t};
|
||||||
|
@ -87,9 +87,13 @@ add_specs suite_builder =
|
|||||||
(x:A).a . should_equal "a"
|
(x:A).a . should_equal "a"
|
||||||
|
|
||||||
call_a obj:A = obj.a
|
call_a obj:A = obj.a
|
||||||
|
call_b obj:B = obj.b
|
||||||
|
|
||||||
# A&X type has attribute a
|
# A&X type has attribute a
|
||||||
call_a ax . should_equal "a"
|
call_a ax . should_equal "a"
|
||||||
|
# A&X can be converted to B
|
||||||
|
call_b ax . should_equal "b"
|
||||||
|
call_b (ax:X&A) . should_equal "b"
|
||||||
|
|
||||||
# according to "static typing" discussion at
|
# according to "static typing" discussion at
|
||||||
# https://github.com/enso-org/enso/pull/11600#discussion_r1867584107
|
# https://github.com/enso-org/enso/pull/11600#discussion_r1867584107
|
||||||
@ -97,10 +101,35 @@ add_specs suite_builder =
|
|||||||
Test.expect_panic Type_Error <|
|
Test.expect_panic Type_Error <|
|
||||||
call_a x . should_equal "a"
|
call_a x . should_equal "a"
|
||||||
|
|
||||||
|
Test.expect_panic Type_Error <|
|
||||||
|
call_b x . should_equal "b"
|
||||||
|
|
||||||
|
call_b (x:X&A) . should_equal "b"
|
||||||
|
|
||||||
# multivalue ax restricted to X cannot be converted to B in to_b_to_c
|
# multivalue ax restricted to X cannot be converted to B in to_b_to_c
|
||||||
Test.expect_panic Type_Error <|
|
Test.expect_panic Type_Error <|
|
||||||
to_b_to_c x
|
to_b_to_c x
|
||||||
|
|
||||||
|
msg1 = case ax of
|
||||||
|
b:B -> "Not a "+b.to_text
|
||||||
|
a:A -> "==="+a.a
|
||||||
|
msg1 . should_equal "===a"
|
||||||
|
|
||||||
|
msg2 = case (ax:X&A) of
|
||||||
|
b:B -> "Not a "+b.to_text
|
||||||
|
a:A -> "==="+a.a
|
||||||
|
msg2 . should_equal "===a"
|
||||||
|
|
||||||
|
msg3 = case x of
|
||||||
|
b:B -> "Not a "+b.to_text
|
||||||
|
a:A -> "==="+a.a
|
||||||
|
msg3 . should_equal "===a"
|
||||||
|
|
||||||
|
msg4 = case x of
|
||||||
|
b:B -> "Not a "+b.to_text
|
||||||
|
a:A -> "A but also "+(a:X).to_text
|
||||||
|
msg4 . should_equal "A but also X"
|
||||||
|
|
||||||
group_builder.specify "Intersection type of unrelated types is not possible" <|
|
group_builder.specify "Intersection type of unrelated types is not possible" <|
|
||||||
Test.expect_panic Type_Error <|
|
Test.expect_panic Type_Error <|
|
||||||
_ = X:X&B
|
_ = X:X&B
|
||||||
|
Loading…
Reference in New Issue
Block a user