diff --git a/CHANGELOG.md b/CHANGELOG.md
index e29c074839b5..8a3973d00e13 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -377,6 +377,8 @@
- [Added support for Date/Time columns in the Postgres backend and added
`year`/`month`/`day` operations to Table columns.][6153]
- [`Text.split` can now take a vector of delimiters.][6156]
+- [Add `has_warnings`, `remove_warnings` and `throw_on_warning` extension
+ methods.][6176]
- [Implemented `Table.union` for the Database backend.][6204]
- [Array & Vector have the same methods & behavior][6218]
@@ -572,6 +574,7 @@
[6150]: https://github.com/enso-org/enso/pull/6150
[6153]: https://github.com/enso-org/enso/pull/6153
[6156]: https://github.com/enso-org/enso/pull/6156
+[6176]: https://github.com/enso-org/enso/pull/6176
[6204]: https://github.com/enso-org/enso/pull/6204
[6077]: https://github.com/enso-org/enso/pull/6077
[6218]: https://github.com/enso-org/enso/pull/6218
diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Any.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Any.enso
index 67455926fc36..6a2984689c05 100644
--- a/distribution/lib/Standard/Base/0.0.0-dev/src/Any.enso
+++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Any.enso
@@ -1,13 +1,15 @@
import project.Data.Pair.Pair
import project.Data.Range.Extensions
import project.Data.Text.Text
+import project.Data.Vector.Vector
import project.Error.Error
import project.Errors.Common.Incomparable_Values
import project.Errors.Common.No_Such_Conversion
import project.Errors.Common.Type_Error
-import project.Nothing.Nothing
import project.Meta
+import project.Nothing.Nothing
import project.Panic.Panic
+import project.Warning.Warning
from project.Data.Boolean import Boolean, True, False
from project.Data.Ordering import all
@@ -450,6 +452,32 @@ type Any
>> : (Any -> Any) -> (Any -> Any)
>> self ~that = x -> that (self x)
+ ## Checks if any warnings (either all or of a specified type) are attached to the value.
+
+ Arguments:
+ - warning_type: The type to check if attached to the value. Defaults to any warning.
+ has_warnings : Any -> Boolean
+ has_warnings self warning_type=Any =
+ _ = warning_type
+ False
+
+ ## Remove the warnings (either all or of a specified type) attached to the value.
+
+ Arguments:
+ - warning_type: The type to remove if attached to the value. Defaults to all warnings.
+ remove_warnings : Any -> Any
+ remove_warnings self warning_type=Any =
+ _ = warning_type
+ self
+
+ ## Throws the first matching warning (either all or of a specified type) as a data flow error.
+
+ Arguments:
+ - warning_type: The type to throw if attached to the value. Defaults to all warnings.
+ throw_on_warning : Any -> Any
+ throw_on_warning self warning_type=Any =
+ _ = warning_type
+ self
## PRIVATE
Checks if the comparators for the given objects are both of the same type. If so,
diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Warning.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Warning.enso
index 70c8289cc962..08aade3b3a4c 100644
--- a/distribution/lib/Standard/Base/0.0.0-dev/src/Warning.enso
+++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Warning.enso
@@ -6,6 +6,7 @@ import project.Data.Numbers.Integer
import project.Data.Pair.Pair
import project.Data.Vector.Vector
import project.Error.Error
+import project.Meta
import project.Nothing.Nothing
import project.Polyglot.Polyglot
import project.Runtime
@@ -28,7 +29,26 @@ type Warning
## ADVANCED
Are any warnings attached to the value?
has_warnings : Any -> Boolean
- has_warnings value = has_warnings_builtin value
+ has_warnings value warning_type=Any =
+ Warning.get_all value . any (w-> w.value.is_a warning_type)
+
+ ## Remove the warnings (either all or of a specified type) attached to the value.
+
+ Arguments:
+ - warning_type: The type to remove if attached to the value. Defaults to all warnings.
+ remove_warnings : Any -> Any
+ remove_warnings value warning_type=Any =
+ Warning.detach_selected_warnings value (w-> w.is_a warning_type) . first
+
+ ## Throws the first matching warning (either all or of a specified type) as a data flow error.
+
+ Arguments:
+ - warning_type: The type to throw if attached to the value. Defaults to all warnings.
+ throw_on_warning : Any -> Any
+ throw_on_warning self warning_type=Any =
+ warnings = Warning.get_all self
+ first = warnings.find (w-> w.value.is_a warning_type) if_missing=Nothing
+ if first.is_nothing then self else Error.throw first.value
## ADVANCED
Gets all the warnings attached to the given value. Warnings are returned in the
@@ -251,12 +271,6 @@ create payload origin = @Builtin_Method "Warning.create"
attach_with_stacktrace : Any -> Any -> Vector Stack_Trace_Element -> Any
attach_with_stacktrace value warning origin = @Builtin_Method "Warning.attach_with_stacktrace"
-## PRIVATE
-
- Builtin function that sees if any warnings attached.
-has_warnings_builtin : Any -> Array Warning
-has_warnings_builtin value = @Builtin_Method "Warning.has_warnings_builtin"
-
## PRIVATE
Builtin function that gets all the warnings attached to the given value.
diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeMethodNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeMethodNode.java
index 79fddcd65b0e..0ccd30812095 100644
--- a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeMethodNode.java
+++ b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeMethodNode.java
@@ -20,15 +20,18 @@
import org.enso.interpreter.node.callable.resolver.MethodResolverNode;
import org.enso.interpreter.node.callable.thunk.ThunkExecutorNode;
import org.enso.interpreter.runtime.EnsoContext;
+import org.enso.interpreter.runtime.builtin.Builtins;
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
import org.enso.interpreter.runtime.callable.function.Function;
+import org.enso.interpreter.runtime.callable.function.FunctionSchema;
import org.enso.interpreter.runtime.data.ArrayRope;
import org.enso.interpreter.runtime.data.EnsoDate;
import org.enso.interpreter.runtime.data.EnsoDateTime;
import org.enso.interpreter.runtime.data.EnsoDuration;
import org.enso.interpreter.runtime.data.EnsoTimeOfDay;
import org.enso.interpreter.runtime.data.EnsoTimeZone;
+import org.enso.interpreter.runtime.data.Type;
import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.error.DataflowError;
import org.enso.interpreter.runtime.error.PanicException;
@@ -51,6 +54,7 @@ public abstract class InvokeMethodNode extends BaseNode {
private @Child InvokeFunctionNode invokeFunctionNode;
private final ConditionProfile errorReceiverProfile = ConditionProfile.createCountingProfile();
private @Child InvokeMethodNode childDispatch;
+
private final int argumentCount;
private final int thisArgumentPosition;
@@ -82,6 +86,10 @@ public static InvokeMethodNode build(
this.thisArgumentPosition = thisArgumentPosition;
}
+ InvokeFunctionNode getInvokeFunctionNode() {
+ return invokeFunctionNode;
+ }
+
@Override
public void setTailStatus(TailStatus tailStatus) {
super.setTailStatus(tailStatus);
@@ -134,13 +142,113 @@ Object doPanicSentinel(
throw self;
}
- @Specialization
+ /**
+ * Resolves symbol to a Warning method, if possible.
+ *
+ *
A regular method dispatch logic will extract/append warnings of `self` before invoking the
+ * actual method dispatch logic. This allows for ignoring complexity related to the presence of
+ * warnings but prevents us from manipulating warnings directly in the Enso code (they have just
+ * been removed). `resolveWarningFunction` will attempt to resolve the symbol in the Warning type
+ * scope, if possible. Additionally, we check if under the non-warning `self`, the symbol would
+ * resolve to `Any`. If not, it means that we should employ a regular method dispatch logic, due
+ * to a method name clash. E.g. if some collection type Foo defines a `has_warnings` method, we
+ * should dispatch the call to `Foo`'s `has_warning` rather than to a `Warning` or `Any`'s `one.
+ *
+ * @param self `self` argument that has some warnings
+ * @param symbol symbol to be resolved
+ * @param types TypesLibrary instance
+ * @param warnings WarningsLibrary instance
+ * @return resolved Warning method to be called
+ */
+ Function resolveWarningFunction(
+ Object self, UnresolvedSymbol symbol, TypesLibrary types, WarningsLibrary warnings) {
+ Object selfWithoutWarnings;
+ try {
+ selfWithoutWarnings = warnings.removeWarnings(self);
+ } catch (UnsupportedMessageException e) {
+ throw CompilerDirectives.shouldNotReachHere(
+ "`self` object should have some warnings when calling `" + symbol.getName() + "` method",
+ e);
+ }
+
+ Type typeOfSymbol = symbol.resolveDeclaringType(types.getType(selfWithoutWarnings));
+ Builtins builtins = EnsoContext.get(this).getBuiltins();
+ if (typeOfSymbol == builtins.any()) {
+ return symbol
+ .getScope()
+ .lookupMethodDefinition(builtins.warning().getEigentype(), symbol.getName());
+ }
+
+ return null;
+ }
+
+ Object[] argumentsWithExplicitSelf(FunctionSchema cachedSchema, Object[] arguments) {
+ Object[] arguments1;
+ if (!cachedSchema.isFullyApplied()) {
+ arguments1 = new Object[cachedSchema.getArgumentsCount()];
+ System.arraycopy(arguments, 0, arguments1, 1, arguments.length);
+ arguments1[0] = arguments[0];
+ } else {
+ arguments1 = arguments;
+ }
+ return arguments1;
+ }
+
+ public InvokeFunctionNode buildInvokeFunctionWithSelf() {
+ int length = invokeFunctionNode.getSchema().length;
+ CallArgumentInfo[] schema = new CallArgumentInfo[length + 1];
+ System.arraycopy(invokeFunctionNode.getSchema(), 0, schema, 1, length);
+ schema[0] = new CallArgumentInfo();
+ return InvokeFunctionNode.build(
+ schema,
+ invokeFunctionNode.getDefaultsExecutionMode(),
+ invokeFunctionNode.getArgumentsExecutionMode());
+ }
+
+ @Specialization(
+ guards = {
+ "warnings.hasWarnings(self)",
+ "resolvedFunction != null",
+ "resolvedFunction.getSchema() == cachedSchema"
+ })
+ Object doWarningsCustom(
+ VirtualFrame frame,
+ State state,
+ UnresolvedSymbol symbol,
+ Object self,
+ Object[] arguments,
+ @CachedLibrary(limit = "10") TypesLibrary types,
+ @CachedLibrary(limit = "10") WarningsLibrary warnings,
+ @Cached("resolveWarningFunction(self, symbol, types, warnings)") Function resolvedFunction,
+ @Cached("resolvedFunction.getSchema()") FunctionSchema cachedSchema,
+ @Cached("buildInvokeFunctionWithSelf()") InvokeFunctionNode warningFunctionNode) {
+ // Warning's builtin type methods are static meaning that they have a synthetic `self`
+ // parameter. However, the constructed `InvokeFunctionNode` is missing that
+ // information and the function, if called with `arguments`, will not be fully applied.
+ // Hence, the synthetic construction of a new `InvokeFunctionNode` with the updated schema
+ // and call including an additional, dummy, argument.
+ Object[] arguments1 = argumentsWithExplicitSelf(cachedSchema, arguments);
+ return warningFunctionNode.execute(resolvedFunction, frame, state, arguments1);
+ }
+
+ @Specialization(guards = "warnings.hasWarnings(self)")
Object doWarning(
VirtualFrame frame,
State state,
UnresolvedSymbol symbol,
- WithWarnings self,
- Object[] arguments) {
+ Object self,
+ Object[] arguments,
+ @CachedLibrary(limit = "10") WarningsLibrary warnings) {
+ Object selfWithoutWarnings;
+ Warning[] arrOfWarnings;
+ try {
+ selfWithoutWarnings = warnings.removeWarnings(self);
+ arrOfWarnings = warnings.getWarnings(self, this);
+ } catch (UnsupportedMessageException e) {
+ // Can't throw `CompilerDirectives.shouldNotReachHere` as it crashes native-image build
+ throw new IllegalStateException(e);
+ }
+
// Cannot use @Cached for childDispatch, because we need to call notifyInserted.
if (childDispatch == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
@@ -164,15 +272,16 @@ Object doWarning(
}
}
- arguments[thisArgumentPosition] = self.getValue();
- ArrayRope warnings = self.getReassignedWarnings(this);
- Object result = childDispatch.execute(frame, state, symbol, self.getValue(), arguments);
- return WithWarnings.prependTo(result, warnings);
+ arguments[thisArgumentPosition] = selfWithoutWarnings;
+
+ Object result = childDispatch.execute(frame, state, symbol, selfWithoutWarnings, arguments);
+ return WithWarnings.prependTo(result, arrOfWarnings);
}
@ExplodeLoop
@Specialization(
guards = {
+ "!warnings.hasWarnings(self)",
"!methods.hasType(self)",
"!methods.hasSpecialDispatch(self)",
"polyglotCallType.isInteropLibrary()",
@@ -225,6 +334,7 @@ Object doPolyglot(
@Specialization(
guards = {
+ "!warnings.hasWarnings(self)",
"!types.hasType(self)",
"!types.hasSpecialDispatch(self)",
"getPolyglotCallType(self, symbol, interop) == CONVERT_TO_TEXT"
@@ -237,6 +347,7 @@ Object doConvertText(
Object[] arguments,
@CachedLibrary(limit = "10") InteropLibrary interop,
@CachedLibrary(limit = "10") TypesLibrary types,
+ @CachedLibrary(limit = "10") WarningsLibrary warnings,
@Cached MethodResolverNode methodResolverNode) {
try {
var str = interop.asString(self);
@@ -253,6 +364,7 @@ Object doConvertText(
@Specialization(
guards = {
+ "!warnings.hasWarnings(self)",
"!types.hasType(self)",
"!types.hasSpecialDispatch(self)",
"getPolyglotCallType(self, symbol, interop, methodResolverNode) == CONVERT_TO_ARRAY",
@@ -265,6 +377,7 @@ Object doConvertArray(
Object[] arguments,
@CachedLibrary(limit = "10") InteropLibrary interop,
@CachedLibrary(limit = "10") TypesLibrary types,
+ @CachedLibrary(limit = "10") WarningsLibrary warnings,
@Cached MethodResolverNode methodResolverNode) {
var ctx = EnsoContext.get(this);
var arrayType = ctx.getBuiltins().array();
@@ -275,6 +388,7 @@ Object doConvertArray(
@Specialization(
guards = {
+ "!warnings.hasWarnings(self)",
"!types.hasType(self)",
"!types.hasSpecialDispatch(self)",
"getPolyglotCallType(self, symbol, interop, methodResolverNode) == CONVERT_TO_HASH_MAP",
@@ -287,6 +401,7 @@ Object doConvertHashMap(
Object[] arguments,
@CachedLibrary(limit = "10") InteropLibrary interop,
@CachedLibrary(limit = "10") TypesLibrary types,
+ @CachedLibrary(limit = "10") WarningsLibrary warnings,
@Cached MethodResolverNode methodResolverNode) {
var ctx = EnsoContext.get(this);
var hashMapType = ctx.getBuiltins().map();
@@ -297,6 +412,7 @@ Object doConvertHashMap(
@Specialization(
guards = {
+ "!warnings.hasWarnings(self)",
"!types.hasType(self)",
"!types.hasSpecialDispatch(self)",
"getPolyglotCallType(self, symbol, interop) == CONVERT_TO_DATE"
@@ -309,6 +425,7 @@ Object doConvertDate(
Object[] arguments,
@CachedLibrary(limit = "10") TypesLibrary types,
@CachedLibrary(limit = "10") InteropLibrary interop,
+ @CachedLibrary(limit = "10") WarningsLibrary warnings,
@Cached MethodResolverNode methodResolverNode) {
var ctx = EnsoContext.get(this);
try {
@@ -325,6 +442,7 @@ Object doConvertDate(
@Specialization(
guards = {
+ "!warnings.hasWarnings(self)",
"!types.hasType(self)",
"!types.hasSpecialDispatch(self)",
"getPolyglotCallType(self, symbol, interop) == CONVERT_TO_DATE_TIME"
@@ -337,6 +455,7 @@ Object doConvertDateTime(
Object[] arguments,
@CachedLibrary(limit = "10") TypesLibrary types,
@CachedLibrary(limit = "10") InteropLibrary interop,
+ @CachedLibrary(limit = "10") WarningsLibrary warnings,
@Cached MethodResolverNode methodResolverNode) {
var ctx = EnsoContext.get(this);
try {
@@ -355,6 +474,7 @@ Object doConvertDateTime(
@Specialization(
guards = {
+ "!warnings.hasWarnings(self)",
"!types.hasType(self)",
"!types.hasSpecialDispatch(self)",
"getPolyglotCallType(self, symbol, interop) == CONVERT_TO_DURATION"
@@ -367,6 +487,7 @@ Object doConvertDuration(
Object[] arguments,
@CachedLibrary(limit = "10") TypesLibrary types,
@CachedLibrary(limit = "10") InteropLibrary interop,
+ @CachedLibrary(limit = "10") WarningsLibrary warnings,
@Cached MethodResolverNode methodResolverNode) {
var ctx = EnsoContext.get(this);
try {
@@ -393,6 +514,7 @@ private ZonedDateTime dateTime(LocalDate date, LocalTime time, ZoneId zone) {
@Specialization(
guards = {
+ "!warnings.hasWarnings(self)",
"!types.hasType(self)",
"!types.hasSpecialDispatch(self)",
"getPolyglotCallType(self, symbol, interop) == CONVERT_TO_ZONED_DATE_TIME"
@@ -405,6 +527,7 @@ Object doConvertZonedDateTime(
Object[] arguments,
@CachedLibrary(limit = "10") TypesLibrary types,
@CachedLibrary(limit = "10") InteropLibrary interop,
+ @CachedLibrary(limit = "10") WarningsLibrary warnings,
@Cached MethodResolverNode methodResolverNode) {
var ctx = EnsoContext.get(this);
try {
@@ -423,6 +546,7 @@ Object doConvertZonedDateTime(
@Specialization(
guards = {
+ "!warnings.hasWarnings(self)",
"!types.hasType(self)",
"!types.hasSpecialDispatch(self)",
"getPolyglotCallType(self, symbol, interop) == CONVERT_TO_TIME_ZONE"
@@ -435,6 +559,7 @@ Object doConvertZone(
Object[] arguments,
@CachedLibrary(limit = "10") TypesLibrary types,
@CachedLibrary(limit = "10") InteropLibrary interop,
+ @CachedLibrary(limit = "10") WarningsLibrary warnings,
@Cached MethodResolverNode methodResolverNode) {
var ctx = EnsoContext.get(this);
try {
@@ -451,6 +576,7 @@ Object doConvertZone(
@Specialization(
guards = {
+ "!warnings.hasWarnings(self)",
"!types.hasType(self)",
"!types.hasSpecialDispatch(self)",
"getPolyglotCallType(self, symbol, interop) == CONVERT_TO_TIME_OF_DAY"
@@ -463,6 +589,7 @@ Object doConvertTimeOfDay(
Object[] arguments,
@CachedLibrary(limit = "10") TypesLibrary types,
@CachedLibrary(limit = "10") InteropLibrary interop,
+ @CachedLibrary(limit = "10") WarningsLibrary warnings,
@Cached MethodResolverNode methodResolverNode) {
var ctx = EnsoContext.get(this);
try {
@@ -479,6 +606,7 @@ Object doConvertTimeOfDay(
@Specialization(
guards = {
+ "!warnings.hasWarnings(self)",
"!methods.hasType(self)",
"!methods.hasSpecialDispatch(self)",
"getPolyglotCallType(self, symbol, interop) == NOT_SUPPORTED"
@@ -491,6 +619,7 @@ Object doFallback(
Object[] arguments,
@CachedLibrary(limit = "10") TypesLibrary methods,
@CachedLibrary(limit = "10") InteropLibrary interop,
+ @CachedLibrary(limit = "10") WarningsLibrary warnings,
@Cached MethodResolverNode anyResolverNode) {
var ctx = EnsoContext.get(this);
Function function = anyResolverNode.expectNonNull(self, ctx.getBuiltins().any(), symbol);
diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/dispatch/InvokeFunctionNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/dispatch/InvokeFunctionNode.java
index f9f5fd5becb5..151c32cccbee 100644
--- a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/dispatch/InvokeFunctionNode.java
+++ b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/dispatch/InvokeFunctionNode.java
@@ -30,7 +30,7 @@
@ImportStatic({CallArgumentInfo.ArgumentMappingBuilder.class, Constants.CacheSizes.class})
public abstract class InvokeFunctionNode extends BaseNode {
- private @CompilationFinal(dimensions = 1) CallArgumentInfo[] schema;
+ private final CallArgumentInfo[] schema;
private final InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode;
private final InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode;
private @Child CaptureCallerInfoNode captureCallerInfoNode = CaptureCallerInfoNode.build();
diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/UnresolvedSymbol.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/UnresolvedSymbol.java
index 58b3ed74e5cf..9dfdecaec14f 100644
--- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/UnresolvedSymbol.java
+++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/UnresolvedSymbol.java
@@ -68,6 +68,24 @@ public Function resolveFor(Type type) {
return null;
}
+ /**
+ * Resolves the type where the symbol is declared.
+ *
+ * @param type the type for which this symbol should be resolved
+ * @return the resolved function definition, or null if not found
+ */
+ public Type resolveDeclaringType(Type type) {
+ Type current = type;
+ while (current != null) {
+ Function candidate = scope.lookupMethodDefinition(current, name);
+ if (candidate != null) {
+ return current;
+ }
+ current = current.getSupertype();
+ }
+ return null;
+ }
+
@Override
public String toString() {
return "UnresolvedSymbol<" + this.name + ">";
diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/error/Warning.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/error/Warning.java
index a010c22528e5..07247bda6da6 100644
--- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/error/Warning.java
+++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/error/Warning.java
@@ -85,25 +85,6 @@ public static WithWarnings attach(EnsoContext ctx, Object value, Object warning,
return new WithWarnings(value, new Warning(warning, origin, ctx.clockTick()));
}
- @Builtin.Method(
- name = "has_warnings_builtin",
- description = "Are any warnings attached to the value.",
- autoRegister = false)
- @Builtin.Specialize
- @CompilerDirectives.TruffleBoundary
- public static Boolean hasWarnings(WithWarnings value, WarningsLibrary warningsLib) {
- return value.hasWarnings();
- }
-
- @Builtin.Method(
- name = "has_warnings_builtin",
- description = "Are any warnings attached to the value.",
- autoRegister = false)
- @Builtin.Specialize(fallback = true)
- public static Boolean hasWarnings(Object value, WarningsLibrary warnings) {
- return warnings.hasWarnings(value);
- }
-
@Builtin.Method(
name = "get_all_array",
description = "Gets all the warnings associated with the value.",
diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/error/WithWarnings.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/error/WithWarnings.java
index ea1536cd23ef..aa3e3bc96cce 100644
--- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/error/WithWarnings.java
+++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/error/WithWarnings.java
@@ -100,6 +100,14 @@ public static WithWarnings prependTo(Object target, ArrayRope warnings)
}
}
+ public static WithWarnings prependTo(Object target, Warning[] warnings) {
+ if (target instanceof WithWarnings) {
+ return ((WithWarnings) target).prepend(warnings);
+ } else {
+ return new WithWarnings(target, warnings);
+ }
+ }
+
@ExportMessage
Object send(Message message, Object[] args, @CachedLibrary(limit = "3") ReflectionLibrary lib)
throws Exception {
diff --git a/test/Tests/src/Semantic/Warnings_Spec.enso b/test/Tests/src/Semantic/Warnings_Spec.enso
index 3d5cdb28b17a..d94fa98f64ae 100644
--- a/test/Tests/src/Semantic/Warnings_Spec.enso
+++ b/test/Tests/src/Semantic/Warnings_Spec.enso
@@ -1,4 +1,5 @@
from Standard.Base import all
+import Standard.Base.Errors.Unimplemented.Unimplemented
polyglot java import java.lang.Long
polyglot java import java.util.function.Function as Java_Function
@@ -17,6 +18,20 @@ My_Type.my_method self = self.a + self.b + self.c
type Wrap
Value foo
+type My_Fancy_Collection
+ Value (x:Integer)
+
+ get_all : Vector Integer
+ get_all self = [self.x]
+
+ has_warnings : Boolean
+ has_warnings self = False
+
+ remove_warnings : Any -> Integer
+ remove_warnings self warning_type=Any =
+ _ = warning_type
+ 42
+
rewrap w = case w of
Wrap.Value a -> Wrap.Value a+1
@@ -149,12 +164,24 @@ spec = Test.group "Dataflow Warnings" <|
rewarned.should_equal 'foo'
Warning.get_all rewarned . map .value . should_contain_the_same_elements_as [2,4]
+ Test.specify "should allow checking for any warnings" <|
+ Warning.has_warnings "foo" . should_be_false
+ "foo".has_warnings.should_be_false
+
+ warned = Warning.attach 1 "foo"
+ warned.has_warnings.should_be_true
+ Warning.has_warnings warned . should_be_true
+
Test.specify "should allow to clear warnings" <|
warned = Warning.attach 1 <| Warning.attach 2 <| Warning.attach 3 <| Warning.attach 4 "foo"
cleared = Warning.clear warned
cleared.should_equal 'foo'
Warning.get_all cleared . map .value . should_equal []
+ clear_2 = warned.remove_warnings
+ clear_2.should_equal 'foo'
+ Warning.get_all clear_2 . map .value . should_equal []
+
Test.specify "should allow to run a function suspending warnings attached to an argument and reattach them to the result" <|
x = Warning.attach 1 <| Warning.attach 2 <| Warning.attach 3 <| Warning.attach 4 "foo"
y = Warning.with_suspended x x->
@@ -194,6 +221,40 @@ spec = Test.group "Dataflow Warnings" <|
Warning.get_all (detached_pair . first) . map .value . should_equal [2,4]
detached_pair.second . map .value . should_equal [1,3]
+ Test.specify "should allow to checking for warnings, by type" <|
+ warned = Warning.attach 1 <| Warning.attach "Alpha" <| Warning.attach Nothing <| Warning.attach (Unimplemented.Error "An Error Here") "foo"
+
+ warned.has_warnings.should_be_true
+ warned.has_warnings warning_type=Integer . should_be_true
+ warned.has_warnings warning_type=Number . should_be_true
+ warned.has_warnings warning_type=Decimal . should_be_false
+ warned.has_warnings warning_type=Text . should_be_true
+ warned.has_warnings warning_type=Nothing . should_be_true
+ warned.has_warnings warning_type=Unimplemented . should_be_true
+
+ Test.specify "should allow to remove warnings, by type" <|
+ warned = Warning.attach 1 <| Warning.attach "Alpha" <| Warning.attach Nothing <| Warning.attach (Unimplemented.Error "An Error Here") "foo"
+
+ no_int = warned.remove_warnings warning_type=Integer . first
+ Warning.get_all no_int . map .value . should_equal ["Alpha", Nothing, (Unimplemented.Error "An Error Here")]
+
+ no_text = warned.remove_warnings Text
+ Warning.get_all no_text . map .value . should_equal [1, Nothing, (Unimplemented.Error "An Error Here")]
+
+ no_nothing = warned.remove_warnings Nothing
+ Warning.get_all no_nothing . map .value . should_equal [1, "Alpha", (Unimplemented.Error "An Error Here")]
+
+ no_error = warned.remove_warnings Unimplemented
+ Warning.get_all no_error . map .value . should_equal [1, "Alpha", Nothing]
+
+ Test.specify "should allow to throwing warnings, by type" <|
+ warned = Warning.attach 1 <| Warning.attach "Alpha" <| Warning.attach Nothing <| Warning.attach (Unimplemented.Error "An Error Here") "foo"
+
+ warned.throw_on_warning . should_fail_with Integer
+ warned.throw_on_warning warning_type=Text . should_fail_with Text
+ warned.throw_on_warning warning_type=Nothing . should_fail_with Nothing
+ warned.throw_on_warning warning_type=Unimplemented . should_fail_with Unimplemented
+
Test.specify "should allow to map the warnings, selectively" <|
x = Warning.attach "foo" 1
result = x.is_static_nothing x
@@ -298,4 +359,16 @@ spec = Test.group "Dataflow Warnings" <|
r4.should_equal Nothing
Warning.get_all r4 . map .value . should_contain_the_same_elements_as ["i(1)", "x"]
+ Test.specify "should not affect method dispatch" <|
+ a = My_Fancy_Collection.Value 42
+ b = Warning.attach "WARN" <| My_Fancy_Collection.Value 23
+
+ a.get_all . should_equal [42]
+ b.get_all . should_equal [23]
+ a.has_warnings . should_equal False
+ b.has_warnings . should_equal False
+ a.remove_warnings . should_equal 42
+ b.remove_warnings . should_equal 42
+
+
main = Test_Suite.run_main spec