mirror of
https://github.com/enso-org/enso.git
synced 2024-12-18 21:41:37 +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 self that epsilon=0 frames_to_skip=0 =
|
||||
matches = case that of
|
||||
_ : Number -> self.equals that epsilon
|
||||
n : Number -> self.equals n epsilon
|
||||
_ -> self==that
|
||||
case matches of
|
||||
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
|
||||
can only be brought back through an _explicit_ cast.
|
||||
To perform an explicit cast that can uncover the 'hidden' part of a type write
|
||||
`f = c:Float`.
|
||||
|
||||
<!--
|
||||
When #11828 is fixed.
|
||||
|
||||
- or inspect the types in a `case` expression, e.g.
|
||||
```
|
||||
case c of
|
||||
f : Float -> f.sqrt
|
||||
_ -> "Not a float"
|
||||
```
|
||||
-->
|
||||
`f = c:Float` or inspect the types in a `case` expression, e.g.
|
||||
```ruby
|
||||
case c of
|
||||
f : Float -> f.sqrt
|
||||
_ -> "Not a float"
|
||||
```
|
||||
Remember to use `f.sqrt` and not `c.sqrt`. `f` in the case branch _has been cast to_ `Float` while
|
||||
`c` in the case branch only _can be cast to_.
|
||||
|
||||
> [!WARNING]
|
||||
> 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();
|
||||
Type[] allTypes = value == null ? null : typeOfNode.findAllTypesOrNull(value);
|
||||
Type[] allTypes = value == null ? null : typeOfNode.findAllTypesOrNull(value, true);
|
||||
if (allTypes != null) {
|
||||
String[] result = new String[allTypes.length];
|
||||
for (var i = 0; i < allTypes.length; i++) {
|
||||
|
@ -49,8 +49,9 @@ public class TypeOfNodeMultiValueTest {
|
||||
new TestRootNode(
|
||||
(frame) -> {
|
||||
var arg = frame.getArguments()[0];
|
||||
var allTypes = (boolean) frame.getArguments()[1];
|
||||
var t = node.findTypeOrError(arg);
|
||||
var all = node.findAllTypesOrNull(arg);
|
||||
var all = node.findAllTypesOrNull(arg, allTypes);
|
||||
return new Object[] {t, all};
|
||||
});
|
||||
root.insertChildren(node);
|
||||
@ -116,16 +117,22 @@ public class TypeOfNodeMultiValueTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void typeOfCheck() {
|
||||
assertType(value, type, typeIndex);
|
||||
public void typeOfCheckAllTypes() {
|
||||
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);
|
||||
ContextUtils.executeInContext(
|
||||
ctx(),
|
||||
() -> {
|
||||
var pairResult = (Object[]) testTypesCall.call(value);
|
||||
var pairResult = (Object[]) testTypesCall.call(value, allTypes);
|
||||
var t = pairResult[0];
|
||||
var all = (Object[]) pairResult[1];
|
||||
|
||||
|
@ -3,8 +3,10 @@ package org.enso.interpreter.node.expression.builtin.meta;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
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.UnresolvedSymbol;
|
||||
import org.enso.interpreter.runtime.data.EnsoMultiValue;
|
||||
import org.enso.interpreter.runtime.data.Type;
|
||||
import org.enso.interpreter.runtime.library.dispatch.TypeOfNode;
|
||||
import org.enso.test.utils.ContextUtils;
|
||||
@ -28,8 +30,9 @@ public class TypeOfNodeValueTest {
|
||||
new TestRootNode(
|
||||
(frame) -> {
|
||||
var arg = frame.getArguments()[0];
|
||||
var allTypes = (boolean) frame.getArguments()[1];
|
||||
var t = node.findTypeOrError(arg);
|
||||
var all = node.findAllTypesOrNull(arg);
|
||||
var all = node.findAllTypesOrNull(arg, allTypes);
|
||||
return new Object[] {t, all};
|
||||
});
|
||||
root.insertChildren(node);
|
||||
@ -54,7 +57,7 @@ public class TypeOfNodeValueTest {
|
||||
ctx(),
|
||||
() -> {
|
||||
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 allTypes = (Type[]) arr[1];
|
||||
assertEquals("Function", type.getName());
|
||||
@ -70,7 +73,7 @@ public class TypeOfNodeValueTest {
|
||||
ctx(),
|
||||
() -> {
|
||||
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 allTypes = (Type[]) arr[1];
|
||||
assertEquals("Function", type.getName());
|
||||
@ -79,4 +82,29 @@ public class TypeOfNodeValueTest {
|
||||
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) {
|
||||
var selfType = findType(typeOfNode, self);
|
||||
if (that instanceof EnsoMultiValue multi) {
|
||||
var all = typeOfNode.findAllTypesOrNull(multi);
|
||||
var all = typeOfNode.findAllTypesOrNull(multi, false);
|
||||
assert all != null;
|
||||
for (var thatType : all) {
|
||||
var fn = findSymbol(symbol, thatType);
|
||||
if (fn != null) {
|
||||
var thatCasted =
|
||||
EnsoMultiValue.CastToNode.getUncached()
|
||||
.findTypeOrNull(thatType, multi, false, false);
|
||||
if (thatCasted == null) {
|
||||
continue;
|
||||
}
|
||||
var result =
|
||||
doDispatch(
|
||||
frame, self, thatCasted, selfType, thatType, fn, convertNode, invokeNode);
|
||||
doDispatch(frame, self, multi, selfType, thatType, fn, convertNode, invokeNode);
|
||||
if (result != null) {
|
||||
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.profiles.CountingConditionProfile;
|
||||
import org.enso.interpreter.node.expression.builtin.meta.IsValueOfTypeNode;
|
||||
import org.enso.interpreter.runtime.data.EnsoMultiValue;
|
||||
import org.enso.interpreter.runtime.data.Type;
|
||||
|
||||
/** 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) {
|
||||
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});
|
||||
}
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ public abstract class CatchPanicNode extends Node {
|
||||
AbstractTruffleException originalException,
|
||||
InteropLibrary interopLibrary) {
|
||||
|
||||
if (profile.profile(isValueOfTypeNode.execute(panicType, payload))) {
|
||||
if (profile.profile(isValueOfTypeNode.execute(panicType, payload, true))) {
|
||||
var builtins = EnsoContext.get(this).getBuiltins();
|
||||
var cons = builtins.caughtPanic().getUniqueConstructor();
|
||||
var caughtPanic =
|
||||
|
@ -4,6 +4,7 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||
import com.oracle.truffle.api.TruffleStackTrace;
|
||||
import com.oracle.truffle.api.TruffleStackTraceElement;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import org.enso.interpreter.dsl.BuiltinMethod;
|
||||
@ -76,6 +77,11 @@ public class GetStackTraceTextNode extends Node {
|
||||
var sourceLoc = errorFrame.getLocation().getEncapsulatingSourceSection();
|
||||
if (sourceLoc != null) {
|
||||
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 loc =
|
||||
(sourceLoc.getStartLine() == sourceLoc.getEndLine())
|
||||
|
@ -331,7 +331,7 @@ abstract class EqualsSimpleNode extends Node {
|
||||
@Shared("multiCast") @Cached EnsoMultiValue.CastToNode castNode,
|
||||
@Shared("multiType") @Cached TypeOfNode typesNode,
|
||||
@Shared("multiEquals") @Cached EqualsSimpleNode delegate) {
|
||||
var types = typesNode.findAllTypesOrNull(self);
|
||||
var types = typesNode.findAllTypesOrNull(self, false);
|
||||
assert types != null;
|
||||
for (var t : types) {
|
||||
var value = castNode.findTypeOrNull(t, self, false, false);
|
||||
|
@ -13,6 +13,6 @@ public class IsANode extends Node {
|
||||
private @Child IsValueOfTypeNode check = IsValueOfTypeNode.build();
|
||||
|
||||
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.TypesLibrary;
|
||||
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. */
|
||||
@NodeInfo(shortName = "IsValueOfTypeNode")
|
||||
public abstract class IsValueOfTypeNode extends Node {
|
||||
IsValueOfTypeNode() {}
|
||||
|
||||
public static IsValueOfTypeNode build() {
|
||||
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)"})
|
||||
boolean doTyped(
|
||||
Object expectedType,
|
||||
Object payload,
|
||||
boolean allTypes,
|
||||
@Shared("types") @CachedLibrary(limit = "3") TypesLibrary types,
|
||||
@Cached Typed typed) {
|
||||
return typed.execute(expectedType, payload);
|
||||
return typed.execute(expectedType, payload, allTypes);
|
||||
}
|
||||
|
||||
@Specialization(guards = {"!types.hasType(payload)"})
|
||||
boolean doPolyglot(
|
||||
Object expectedType,
|
||||
Object payload,
|
||||
boolean allTypes,
|
||||
@Shared("types") @CachedLibrary(limit = "3") TypesLibrary types,
|
||||
@Cached Untyped untyped) {
|
||||
return untyped.execute(expectedType, payload);
|
||||
return untyped.execute(expectedType, payload, allTypes);
|
||||
}
|
||||
|
||||
private static boolean typeAndCheck(
|
||||
Object payload,
|
||||
Object expectedType,
|
||||
Object expectedMeta,
|
||||
boolean allTypes,
|
||||
TypeOfNode typeOfNode,
|
||||
IsSameObjectNode isSameObject,
|
||||
CountingConditionProfile isSameObjectProfile) {
|
||||
Object tpeOfPayload = typeOfNode.findTypeOrError(payload);
|
||||
if (isSameObjectProfile.profile(isSameObject.execute(expectedType, tpeOfPayload))) {
|
||||
return true;
|
||||
} else if (TypesGen.isType(tpeOfPayload)) {
|
||||
Type tpe = TypesGen.asType(tpeOfPayload);
|
||||
var ctx = EnsoContext.get(typeOfNode);
|
||||
for (var superTpe : tpe.allTypes(ctx)) {
|
||||
boolean testSuperTpe = isSameObject.execute(expectedType, superTpe);
|
||||
if (testSuperTpe) {
|
||||
if (expectedMeta instanceof Type expectedType) {
|
||||
var arr = typeOfNode.findAllTypesOrNull(payload, allTypes);
|
||||
if (arr == null) {
|
||||
return false;
|
||||
}
|
||||
for (var tpeOfPayload : arr) {
|
||||
if (isSameObjectProfile.profile(isSameObject.execute(expectedType, tpeOfPayload))) {
|
||||
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;
|
||||
}
|
||||
@ -73,33 +92,33 @@ public abstract class IsValueOfTypeNode extends Node {
|
||||
private @Child TypeOfNode typeOfNode = TypeOfNode.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)")
|
||||
boolean doAnyType(Object expectedType, Object payload) {
|
||||
boolean doAnyType(Object expectedType, Object payload, boolean allTypes) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Specialization
|
||||
boolean doLongCheck(Type expectedType, long payload) {
|
||||
boolean doLongCheck(Type expectedType, long payload, boolean allTypes) {
|
||||
var numbers = EnsoContext.get(this).getBuiltins().number();
|
||||
return checkParentTypes(numbers.getInteger(), expectedType);
|
||||
}
|
||||
|
||||
@Specialization
|
||||
boolean doDoubleCheck(Type expectedType, double payload) {
|
||||
boolean doDoubleCheck(Type expectedType, double payload, boolean allTypes) {
|
||||
var numbers = EnsoContext.get(this).getBuiltins().number();
|
||||
return checkParentTypes(numbers.getFloat(), expectedType);
|
||||
}
|
||||
|
||||
@Specialization
|
||||
boolean doBigIntegerCheck(Type expectedType, EnsoBigInteger value) {
|
||||
boolean doBigIntegerCheck(Type expectedType, EnsoBigInteger value, boolean allTypes) {
|
||||
var numbers = EnsoContext.get(this).getBuiltins().number();
|
||||
return checkParentTypes(numbers.getInteger(), expectedType);
|
||||
}
|
||||
|
||||
@Specialization
|
||||
boolean doUnresolvedSymbol(Type expectedType, UnresolvedSymbol value) {
|
||||
boolean doUnresolvedSymbol(Type expectedType, UnresolvedSymbol value, boolean allTypes) {
|
||||
var funTpe = EnsoContext.get(this).getBuiltins().function();
|
||||
return expectedType == funTpe;
|
||||
}
|
||||
@ -119,8 +138,9 @@ public abstract class IsValueOfTypeNode extends Node {
|
||||
boolean doType(
|
||||
Type expectedType,
|
||||
Object payload,
|
||||
boolean allTypes,
|
||||
@Shared("types") @CachedLibrary(limit = "3") TypesLibrary types) {
|
||||
return typeAndCheck(payload, expectedType, typeOfNode, isSameObject, profile);
|
||||
return typeAndCheck(payload, expectedType, allTypes, typeOfNode, isSameObject, profile);
|
||||
}
|
||||
|
||||
@Specialization(
|
||||
@ -131,13 +151,14 @@ public abstract class IsValueOfTypeNode extends Node {
|
||||
public boolean doArrayViaType(
|
||||
Object expectedType,
|
||||
Object payload,
|
||||
boolean allTypes,
|
||||
@CachedLibrary(limit = "3") InteropLibrary interop,
|
||||
@Shared("types") @CachedLibrary(limit = "3") TypesLibrary types) {
|
||||
return EnsoContext.get(this).getBuiltins().array() == types.getType(payload);
|
||||
}
|
||||
|
||||
@Fallback
|
||||
boolean doOther(Object expectedType, Object payload) {
|
||||
boolean doOther(Object expectedType, Object payload, boolean allTypes) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -155,7 +176,7 @@ public abstract class IsValueOfTypeNode extends Node {
|
||||
private @Child TypeOfNode typeOfNode = TypeOfNode.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 = {
|
||||
@ -163,7 +184,10 @@ public abstract class IsValueOfTypeNode extends Node {
|
||||
"isMetaInstance(interop, expectedType, payload)"
|
||||
})
|
||||
boolean doPolyglotType(
|
||||
Object expectedType, Object payload, @CachedLibrary(limit = "3") InteropLibrary interop) {
|
||||
Object expectedType,
|
||||
Object payload,
|
||||
boolean allTypes,
|
||||
@CachedLibrary(limit = "3") InteropLibrary interop) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -176,8 +200,8 @@ public abstract class IsValueOfTypeNode extends Node {
|
||||
}
|
||||
|
||||
@Fallback
|
||||
public boolean doOther(Object expectedType, Object payload) {
|
||||
return typeAndCheck(payload, expectedType, typeOfNode, isSameObject, profile);
|
||||
public boolean doOther(Object expectedType, Object payload, boolean allTypes) {
|
||||
return typeAndCheck(payload, expectedType, allTypes, typeOfNode, isSameObject, profile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ non-sealed abstract class MetaTypeCheckNode extends AbstractTypeCheckNode {
|
||||
if (isAllFitValue(v)) {
|
||||
return v;
|
||||
}
|
||||
if (isA.execute(expectedSupplier.get(), v)) {
|
||||
if (isA.execute(expectedSupplier.get(), v, true)) {
|
||||
return v;
|
||||
} else {
|
||||
return null;
|
||||
|
@ -123,7 +123,7 @@ non-sealed abstract class SingleTypeCheckNode extends AbstractTypeCheckNode {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
if (checkType.execute(expectedType, v)) {
|
||||
if (checkType.execute(expectedType, v, isAllTypes())) {
|
||||
return v;
|
||||
}
|
||||
return null;
|
||||
@ -176,7 +176,7 @@ non-sealed abstract class SingleTypeCheckNode extends AbstractTypeCheckNode {
|
||||
|
||||
Type[] findType(TypeOfNode typeOfNode, Object v, Type[] previous) {
|
||||
if (v instanceof EnsoMultiValue multi) {
|
||||
var all = typeOfNode.findAllTypesOrNull(multi);
|
||||
var all = typeOfNode.findAllTypesOrNull(multi, isAllTypes());
|
||||
assert all != null;
|
||||
var to = 0;
|
||||
// only consider methodDispatchTypes and not "all types" of the multi value
|
||||
|
@ -88,8 +88,12 @@ public final class EnsoMultiValue extends EnsoObject {
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
final Type[] allTypes() {
|
||||
return types.clone();
|
||||
final Type[] allTypes(boolean includeExtraTypes) {
|
||||
if (includeExtraTypes || methodDispatchTypes == types.length) {
|
||||
return types.clone();
|
||||
} else {
|
||||
return Arrays.copyOf(types, methodDispatchTypes);
|
||||
}
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
@ -452,19 +456,22 @@ public final class EnsoMultiValue extends EnsoObject {
|
||||
|
||||
@Specialization
|
||||
Object castsToAType(Type type, EnsoMultiValue mv, boolean reorderOnly, boolean allTypes) {
|
||||
var ctx = EnsoContext.get(this);
|
||||
var max = allTypes ? mv.types.length : mv.methodDispatchTypes;
|
||||
for (var i = 0; i < max; i++) {
|
||||
if (mv.types[i] == type) {
|
||||
if (reorderOnly) {
|
||||
var copyTypes = mv.types.clone();
|
||||
var copyValues = mv.values.clone();
|
||||
copyTypes[i] = mv.types[0];
|
||||
copyValues[i] = mv.values[0];
|
||||
copyTypes[0] = mv.types[i];
|
||||
copyValues[0] = mv.values[i];
|
||||
return EnsoMultiValue.create(copyTypes, 1, copyValues);
|
||||
} else {
|
||||
return mv.values[i];
|
||||
for (var t : mv.types[i].allTypes(ctx)) {
|
||||
if (t == type) {
|
||||
if (reorderOnly) {
|
||||
var copyTypes = mv.types.clone();
|
||||
var copyValues = mv.values.clone();
|
||||
copyTypes[i] = mv.types[0];
|
||||
copyValues[i] = mv.values[0];
|
||||
copyTypes[0] = mv.types[i];
|
||||
copyValues[0] = mv.values[i];
|
||||
return EnsoMultiValue.create(copyTypes, 1, copyValues);
|
||||
} else {
|
||||
return mv.values[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ public abstract class TypeOfNode extends Node {
|
||||
*/
|
||||
public final TruffleObject findTypeOrError(Object value) {
|
||||
try {
|
||||
var types = executeTypes(value);
|
||||
var types = executeTypes(value, false);
|
||||
return types[0];
|
||||
} catch (NonTypeResult plain) {
|
||||
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}.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
public final Type[] findAllTypesOrNull(Object value) {
|
||||
public final Type[] findAllTypesOrNull(Object value, boolean includeExtraTypes) {
|
||||
try {
|
||||
var types = executeTypes(value);
|
||||
var types = executeTypes(value, includeExtraTypes);
|
||||
assert types != null && types.length > 0;
|
||||
return types;
|
||||
} catch (NonTypeResult ex) {
|
||||
@ -92,11 +94,13 @@ public abstract class TypeOfNode extends Node {
|
||||
* Internal implementation call to delegate to various {@link Specialization} methods.
|
||||
*
|
||||
* @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
|
||||
* @throws NonTypeResult when there is a <em>interop value</em> result, but it is not a {@link
|
||||
* Type}
|
||||
*/
|
||||
abstract Type[] executeTypes(Object value) throws NonTypeResult;
|
||||
abstract Type[] executeTypes(Object value, boolean includeExtraTypes) throws NonTypeResult;
|
||||
|
||||
/**
|
||||
* Creates new optimizing instance of this node.
|
||||
@ -123,38 +127,39 @@ public abstract class TypeOfNode extends Node {
|
||||
}
|
||||
|
||||
@Specialization
|
||||
Type[] doUnresolvedSymbol(UnresolvedSymbol value) {
|
||||
Type[] doUnresolvedSymbol(UnresolvedSymbol value, boolean allTypes) {
|
||||
return fromType(EnsoContext.get(this).getBuiltins().function());
|
||||
}
|
||||
|
||||
@Specialization
|
||||
Type[] doDouble(double value) {
|
||||
Type[] doDouble(double value, boolean allTypes) {
|
||||
return fromType(EnsoContext.get(this).getBuiltins().number().getFloat());
|
||||
}
|
||||
|
||||
@Specialization
|
||||
Type[] doLong(long value) {
|
||||
Type[] doLong(long value, boolean allTypes) {
|
||||
return fromType(EnsoContext.get(this).getBuiltins().number().getInteger());
|
||||
}
|
||||
|
||||
@Specialization
|
||||
Type[] doBigInteger(EnsoBigInteger value) {
|
||||
Type[] doBigInteger(EnsoBigInteger value, boolean allTypes) {
|
||||
return fromType(EnsoContext.get(this).getBuiltins().number().getInteger());
|
||||
}
|
||||
|
||||
@Specialization
|
||||
Type[] doPanicException(PanicException value) {
|
||||
Type[] doPanicException(PanicException value, boolean allTypes) {
|
||||
return fromType(EnsoContext.get(this).getBuiltins().panic());
|
||||
}
|
||||
|
||||
@Specialization
|
||||
Type[] doPanicSentinel(PanicSentinel value) {
|
||||
Type[] doPanicSentinel(PanicSentinel value, boolean allTypes) {
|
||||
return fromType(EnsoContext.get(this).getBuiltins().panic());
|
||||
}
|
||||
|
||||
@Specialization
|
||||
Type[] doWarning(WithWarnings value, @Cached TypeOfNode withoutWarning) throws NonTypeResult {
|
||||
return withoutWarning.executeTypes(value.getValue());
|
||||
Type[] doWarning(WithWarnings value, boolean includeExtraTypes, @Cached TypeOfNode withoutWarning)
|
||||
throws NonTypeResult {
|
||||
return withoutWarning.executeTypes(value.getValue(), includeExtraTypes);
|
||||
}
|
||||
|
||||
static boolean isWithType(Object value, TypesLibrary types, InteropLibrary iop) {
|
||||
@ -180,6 +185,7 @@ public abstract class TypeOfNode extends Node {
|
||||
@Specialization(guards = {"isWithoutType(value, types)"})
|
||||
Type[] withoutType(
|
||||
Object value,
|
||||
boolean allTypes,
|
||||
@Shared("interop") @CachedLibrary(limit = "3") InteropLibrary interop,
|
||||
@Shared("types") @CachedLibrary(limit = "3") TypesLibrary types,
|
||||
@Cached WithoutType delegate)
|
||||
@ -201,14 +207,15 @@ public abstract class TypeOfNode extends Node {
|
||||
@Specialization(guards = {"isWithType(value, types, interop)"})
|
||||
Type[] doType(
|
||||
Object value,
|
||||
boolean includeExtraTypes,
|
||||
@Shared("interop") @CachedLibrary(limit = "3") InteropLibrary interop,
|
||||
@Shared("types") @CachedLibrary(limit = "3") TypesLibrary types) {
|
||||
return types.allTypes(value);
|
||||
return types.allTypes(value, includeExtraTypes);
|
||||
}
|
||||
|
||||
@Fallback
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
Type[] doAny(Object value) throws NonTypeResult {
|
||||
Type[] doAny(Object value, boolean allTypes) throws NonTypeResult {
|
||||
var err =
|
||||
DataflowError.withDefaultTrace(
|
||||
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
|
||||
*
|
||||
* @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}
|
||||
*/
|
||||
public Type[] allTypes(Object receiver) {
|
||||
public Type[] allTypes(Object receiver, boolean includeExtraTypes) {
|
||||
var t = getType(receiver);
|
||||
assert t != null : "null type for " + receiver;
|
||||
return new Type[] {t};
|
||||
|
@ -87,9 +87,13 @@ add_specs suite_builder =
|
||||
(x:A).a . should_equal "a"
|
||||
|
||||
call_a obj:A = obj.a
|
||||
call_b obj:B = obj.b
|
||||
|
||||
# A&X type has attribute 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
|
||||
# https://github.com/enso-org/enso/pull/11600#discussion_r1867584107
|
||||
@ -97,10 +101,35 @@ add_specs suite_builder =
|
||||
Test.expect_panic Type_Error <|
|
||||
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
|
||||
Test.expect_panic Type_Error <|
|
||||
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" <|
|
||||
Test.expect_panic Type_Error <|
|
||||
_ = X:X&B
|
||||
|
Loading…
Reference in New Issue
Block a user