WithWarnings uses EnsoHashMap to speed things up (#10555)

Majority of warnings handling is now done via newly introduced nodes. Moreover, the underlying representation of warnings storage in `WithWarnings` was changed from `Warning[]` to `EnsoHashMap`.

# Important Notes
- Remove `ArrayRope`.
This commit is contained in:
Pavel Marek 2024-08-07 17:29:57 +02:00 committed by GitHub
parent 3a4b942aae
commit c2c6712b77
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
59 changed files with 1537 additions and 1160 deletions

View File

@ -1422,6 +1422,7 @@ lazy val `interpreter-dsl-test` =
) )
.dependsOn(`interpreter-dsl`) .dependsOn(`interpreter-dsl`)
.dependsOn(`runtime`) .dependsOn(`runtime`)
.dependsOn(`test-utils`)
// ============================================================================ // ============================================================================
// === Sub-Projects =========================================================== // === Sub-Projects ===========================================================

View File

@ -255,36 +255,6 @@ type Warning
origin : Vector Stack_Trace_Element origin : Vector Stack_Trace_Element
origin self = @Builtin_Method "Warning.origin" origin self = @Builtin_Method "Warning.origin"
## PRIVATE
ADVANCED
A list of locations where the warning was reassigned in the order of
latest-first.
Warnings are reassigned whenever they interact with specific language
elements:
- When pattern matching, the warnings of the scrutinee will be reassigned
to the `case` expression result.
- When calling a method, warnings assigned to `self` will be reassigned to
the method return value.
- When calling a polyglot function or method, warnings assigned to any
arguments will be accumulated in the return value.
- The standard library methods reassign warnings such that their dataflow
nature is preserved.
reassignments : Vector Stack_Trace_Element
reassignments self =
Vector.from_polyglot_array self.get_reassignments . map r->
loc = case Polyglot.has_source_location r of
False -> Nothing
True -> Source_Location.Value (Polyglot.get_source_location r)
Stack_Trace_Element.Value (Polyglot.get_executable_name r) loc
## PRIVATE
Builtin method for getting the list of locations where the warnings was reassigned.
Should use `Warning.reassignments` instead.
get_reassignments : Array Any
get_reassignments self = @Builtin_Method "Warning.get_reassignments"
## PRIVATE ## PRIVATE

View File

@ -8,6 +8,7 @@ import com.oracle.truffle.api.nodes.DirectCallNode;
import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.api.nodes.RootNode;
import org.enso.interpreter.node.InlineableNode; import org.enso.interpreter.node.InlineableNode;
import org.enso.interpreter.runtime.callable.function.Function; import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.test.utils.ContextUtils;
import org.junit.Test; import org.junit.Test;
public class InliningBuiltinsTest { public class InliningBuiltinsTest {
@ -17,26 +18,33 @@ public class InliningBuiltinsTest {
*/ */
@Test @Test
public void executeWithoutVirtualFrame() { public void executeWithoutVirtualFrame() {
var fn = InliningBuiltinsInMethodGen.makeFunction(null); try (var ctx = ContextUtils.createDefaultContext()) {
if (fn.getCallTarget().getRootNode() instanceof InlineableNode.Root root) { ContextUtils.executeInContext(
var call = root.createInlineableNode(); ctx,
var clazz = call.getClass(); () -> {
assertEquals("InlineableNode", clazz.getSuperclass().getSimpleName()); var fn = InliningBuiltinsInMethodGen.makeFunction(null);
assertEquals( if (fn.getCallTarget().getRootNode() instanceof InlineableNode.Root root) {
"org.enso.interpreter.node.InlineableNode$Root", var call = root.createInlineableNode();
clazz.getEnclosingClass().getInterfaces()[0].getName()); var clazz = call.getClass();
assertEquals("InlineableNode", clazz.getSuperclass().getSimpleName());
assertEquals(
"org.enso.interpreter.node.InlineableNode$Root",
clazz.getEnclosingClass().getInterfaces()[0].getName());
var res = var res =
WithFrame.invoke( WithFrame.invoke(
(frame) -> { (frame) -> {
return call.call( return call.call(
frame, frame,
Function.ArgumentsHelper.buildArguments( Function.ArgumentsHelper.buildArguments(
null, null, new Object[] {null, 5L, 7L})); null, null, new Object[] {null, 5L, 7L}));
}); });
assertEquals(12L, res); assertEquals(12L, res);
} else { } else {
fail("It is inlineable: " + fn.getCallTarget().getRootNode()); fail("It is inlineable: " + fn.getCallTarget().getRootNode());
}
return null;
});
} }
} }
@ -45,22 +53,29 @@ public class InliningBuiltinsTest {
*/ */
@Test @Test
public void executeWithVirtualFrame() { public void executeWithVirtualFrame() {
var fn = InliningBuiltinsOutMethodGen.makeFunction(null); try (var ctx = ContextUtils.createDefaultContext()) {
if (fn.getCallTarget().getRootNode() instanceof InlineableNode.Root root) { ContextUtils.executeInContext(
fail("The node isn't inlineable: " + fn.getCallTarget().getRootNode()); ctx,
} else { () -> {
var call = DirectCallNode.create(fn.getCallTarget()); var fn = InliningBuiltinsOutMethodGen.makeFunction(null);
var clazz = call.getClass().getSuperclass(); if (fn.getCallTarget().getRootNode() instanceof InlineableNode.Root root) {
assertEquals("com.oracle.truffle.api.nodes.DirectCallNode", clazz.getName()); fail("The node isn't inlineable: " + fn.getCallTarget().getRootNode());
} else {
var call = DirectCallNode.create(fn.getCallTarget());
var clazz = call.getClass().getSuperclass();
assertEquals("com.oracle.truffle.api.nodes.DirectCallNode", clazz.getName());
var res = var res =
WithFrame.invoke( WithFrame.invoke(
(frame) -> { (frame) -> {
return call.call( return call.call(
Function.ArgumentsHelper.buildArguments( Function.ArgumentsHelper.buildArguments(
null, null, new Object[] {null, 3L, 9L})); null, null, new Object[] {null, 3L, 9L}));
}); });
assertEquals(12L, res); assertEquals(12L, res);
}
return null;
});
} }
} }
@ -69,22 +84,29 @@ public class InliningBuiltinsTest {
*/ */
@Test @Test
public void executeWhenNeedsVirtualFrame() { public void executeWhenNeedsVirtualFrame() {
var fn = InliningBuiltinsNeedsMethodGen.makeFunction(null); try (var ctx = ContextUtils.createDefaultContext()) {
if (fn.getCallTarget().getRootNode() instanceof InlineableNode.Root root) { ContextUtils.executeInContext(
fail("The node isn't inlineable: " + fn.getCallTarget().getRootNode()); ctx,
} else { () -> {
var call = DirectCallNode.create(fn.getCallTarget()); var fn = InliningBuiltinsNeedsMethodGen.makeFunction(null);
var clazz = call.getClass().getSuperclass(); if (fn.getCallTarget().getRootNode() instanceof InlineableNode.Root root) {
assertEquals("com.oracle.truffle.api.nodes.DirectCallNode", clazz.getName()); fail("The node isn't inlineable: " + fn.getCallTarget().getRootNode());
} else {
var call = DirectCallNode.create(fn.getCallTarget());
var clazz = call.getClass().getSuperclass();
assertEquals("com.oracle.truffle.api.nodes.DirectCallNode", clazz.getName());
var res = var res =
WithFrame.invoke( WithFrame.invoke(
(frame) -> { (frame) -> {
return call.call( return call.call(
Function.ArgumentsHelper.buildArguments( Function.ArgumentsHelper.buildArguments(
null, null, new Object[] {null, 3L, 9L})); null, null, new Object[] {null, 3L, 9L}));
}); });
assertEquals(12L, res); assertEquals(12L, res);
}
return null;
});
} }
} }
@ -93,26 +115,33 @@ public class InliningBuiltinsTest {
*/ */
@Test @Test
public void executeWhenNeedNotVirtualFrame() { public void executeWhenNeedNotVirtualFrame() {
var fn = InliningBuiltinsNeedNotMethodGen.makeFunction(null); try (var ctx = ContextUtils.createDefaultContext()) {
if (fn.getCallTarget().getRootNode() instanceof InlineableNode.Root root) { ContextUtils.executeInContext(
var call = root.createInlineableNode(); ctx,
var clazz = call.getClass(); () -> {
assertEquals("InlineableNode", clazz.getSuperclass().getSimpleName()); var fn = InliningBuiltinsNeedNotMethodGen.makeFunction(null);
assertEquals( if (fn.getCallTarget().getRootNode() instanceof InlineableNode.Root root) {
"org.enso.interpreter.node.InlineableNode$Root", var call = root.createInlineableNode();
clazz.getEnclosingClass().getInterfaces()[0].getName()); var clazz = call.getClass();
assertEquals("InlineableNode", clazz.getSuperclass().getSimpleName());
assertEquals(
"org.enso.interpreter.node.InlineableNode$Root",
clazz.getEnclosingClass().getInterfaces()[0].getName());
var res = var res =
WithFrame.invoke( WithFrame.invoke(
(frame) -> { (frame) -> {
return call.call( return call.call(
frame, frame,
Function.ArgumentsHelper.buildArguments( Function.ArgumentsHelper.buildArguments(
null, null, new Object[] {null, 5L, 7L})); null, null, new Object[] {null, 5L, 7L}));
}); });
assertEquals(12L, res); assertEquals(12L, res);
} else { } else {
fail("It is inlineable: " + fn.getCallTarget().getRootNode()); fail("It is inlineable: " + fn.getCallTarget().getRootNode());
}
return null;
});
} }
} }

View File

@ -27,7 +27,7 @@ public final class Utils {
.option( .option(
RuntimeOptions.LANGUAGE_HOME_OVERRIDE, RuntimeOptions.LANGUAGE_HOME_OVERRIDE,
Paths.get("../../distribution/component").toFile().getAbsolutePath()) Paths.get("../../distribution/component").toFile().getAbsolutePath())
.option(RuntimeOptions.LOG_LEVEL, Level.WARNING.getName()) .option(RuntimeOptions.LOG_LEVEL, Level.INFO.getName())
.option(RuntimeOptions.DISABLE_IR_CACHES, "true") .option(RuntimeOptions.DISABLE_IR_CACHES, "true")
.option(RuntimeOptions.STRICT_ERRORS, "true") .option(RuntimeOptions.STRICT_ERRORS, "true")
.option("engine.CompilationFailureAction", "Print") .option("engine.CompilationFailureAction", "Print")

View File

@ -73,29 +73,34 @@ public class WarningBenchmarks {
benchmarkName = SrcUtil.findName(params); benchmarkName = SrcUtil.findName(params);
var code = var benchCode =
new StringBuilder( """
"""
from Standard.Base import all from Standard.Base import all
vec_sum_bench : Vector Integer -> Integer vec_sum_bench : Vector Integer -> Integer
vec_sum_bench vec = vec_sum_bench vec =
vec.fold 0 (x->y->x+y) vec.fold 0 (x->y->x+y)
""";
create_vec size elem = var setupCode =
new StringBuilder(
"""
from Standard.Base import all
create_vec size elem = Runtime.no_inline <|
Vector.fill size elem Vector.fill size elem
elem = elem =
42 42
elem_const_with_warning = elem_const_with_warning = Runtime.no_inline <|
x = 42 x = 42
Warning.attach "Foo!" x Warning.attach "Foo!" x
elem_with_warning v = elem_with_warning v = Runtime.no_inline <|
Warning.attach "Foo!" v Warning.attach "Foo!" v
map_vector_with_warnings vec = map_vector_with_warnings vec = Runtime.no_inline <|
vec.map (e-> elem_with_warning e) vec.map (e-> elem_with_warning e)
"""); """);
@ -103,31 +108,37 @@ public class WarningBenchmarks {
var randomIntVectorName = "vector_with_random_values"; var randomIntVectorName = "vector_with_random_values";
var vectorWithRandomValues = var vectorWithRandomValues =
generateRandomVector(random, randomIntVectorName, INPUT_DIFF_VEC_SIZE, 3_000); generateRandomVector(random, randomIntVectorName, INPUT_DIFF_VEC_SIZE, 3_000);
code.append(vectorWithRandomValues.repr()); setupCode.append(vectorWithRandomValues.repr());
randomVectorSum = vectorWithRandomValues.sum(); randomVectorSum = vectorWithRandomValues.sum();
var src = SrcUtil.source(benchmarkName, code.toString()); var setupSrc = SrcUtil.source(benchmarkName + "_Setup", setupCode.toString());
Value module = ctx.eval(src); Value setupModule = ctx.eval(setupSrc);
vecSumBench =
Objects.requireNonNull(
module.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "vec_sum_bench"));
createVec = createVec =
Objects.requireNonNull( Objects.requireNonNull(
module.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "create_vec")); setupModule.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "create_vec"));
mapVecWithWarnings = mapVecWithWarnings =
Objects.requireNonNull( Objects.requireNonNull(
module.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "map_vector_with_warnings")); setupModule.invokeMember(
MethodNames.Module.EVAL_EXPRESSION, "map_vector_with_warnings"));
constElem = constElem =
Objects.requireNonNull(module.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "elem")); Objects.requireNonNull(
setupModule.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "elem"));
constElemWithWarning = constElemWithWarning =
Objects.requireNonNull( Objects.requireNonNull(
module.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "elem_const_with_warning")); setupModule.invokeMember(
MethodNames.Module.EVAL_EXPRESSION, "elem_const_with_warning"));
noWarningsVec = createVec.execute(INPUT_VEC_SIZE, constElem); noWarningsVec = createVec.execute(INPUT_VEC_SIZE, constElem);
sameWarningVec = createVec.execute(INPUT_VEC_SIZE, constElemWithWarning); sameWarningVec = createVec.execute(INPUT_VEC_SIZE, constElemWithWarning);
randomVec = randomVec =
Objects.requireNonNull( Objects.requireNonNull(
module.invokeMember(MethodNames.Module.EVAL_EXPRESSION, randomIntVectorName)); setupModule.invokeMember(MethodNames.Module.EVAL_EXPRESSION, randomIntVectorName));
randomElemsWithWarningsVec = mapVecWithWarnings.execute(randomVec); randomElemsWithWarningsVec = mapVecWithWarnings.execute(randomVec);
var benchSrc = SrcUtil.source(benchmarkName + "_Bench", benchCode);
Value benchModule = ctx.eval(benchSrc);
vecSumBench =
Objects.requireNonNull(
benchModule.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "vec_sum_bench"));
} }
@TearDown @TearDown

View File

@ -5,7 +5,7 @@ import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.UnsupportedMessageException; import com.oracle.truffle.api.interop.UnsupportedMessageException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import org.enso.interpreter.runtime.data.text.Text; import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.error.WithWarnings; import org.enso.interpreter.runtime.warning.WithWarnings;
public final class VisualizationResult { public final class VisualizationResult {
private VisualizationResult() {} private VisualizationResult() {}

View File

@ -22,12 +22,7 @@ import org.enso.interpreter.runtime.`type`.{Types, TypesGen}
import org.enso.interpreter.runtime.data.atom.AtomConstructor import org.enso.interpreter.runtime.data.atom.AtomConstructor
import org.enso.interpreter.runtime.callable.function.Function import org.enso.interpreter.runtime.callable.function.Function
import org.enso.interpreter.runtime.control.ThreadInterruptedException import org.enso.interpreter.runtime.control.ThreadInterruptedException
import org.enso.interpreter.runtime.error.{ import org.enso.interpreter.runtime.error.{DataflowError, PanicSentinel}
DataflowError,
PanicSentinel,
WarningsLibrary,
WithWarnings
}
import org.enso.interpreter.service.ExecutionService.{ import org.enso.interpreter.service.ExecutionService.{
ExpressionCall, ExpressionCall,
ExpressionValue, ExpressionValue,
@ -41,6 +36,11 @@ import org.enso.interpreter.service.error.{
VisualizationException VisualizationException
} }
import org.enso.common.LanguageInfo import org.enso.common.LanguageInfo
import org.enso.interpreter.runtime.warning.{
Warning,
WarningsLibrary,
WithWarnings
}
import org.enso.polyglot.debugger.ExecutedVisualization import org.enso.polyglot.debugger.ExecutedVisualization
import org.enso.polyglot.runtime.Runtime.Api import org.enso.polyglot.runtime.Runtime.Api
import org.enso.polyglot.runtime.Runtime.Api.{ContextId, ExecutionResult} import org.enso.polyglot.runtime.Runtime.Api.{ContextId, ExecutionResult}
@ -419,12 +419,9 @@ object ProgramExecutionSupport {
value.getValue value.getValue
) )
) { ) {
val warnings = val warnsMap =
WarningsLibrary.getUncached.getWarnings( WarningsLibrary.getUncached.getWarnings(value.getValue, false)
value.getValue, val warnings = Warning.fromMapToArray(warnsMap)
null,
false
)
val warningsCount = warnings.length val warningsCount = warnings.length
val warning = val warning =
if (warningsCount > 0) { if (warningsCount > 0) {

View File

@ -6,12 +6,11 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import org.enso.common.LanguageInfo;
import org.enso.common.MethodNames; import org.enso.common.MethodNames;
import org.enso.interpreter.runtime.EnsoContext; import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.error.Warning; import org.enso.interpreter.runtime.warning.AppendWarningNode;
import org.enso.interpreter.runtime.error.WarningsLibrary; import org.enso.interpreter.runtime.warning.Warning;
import org.enso.interpreter.runtime.error.WithWarnings; import org.enso.interpreter.runtime.warning.WithWarnings;
import org.enso.test.utils.ContextUtils; import org.enso.test.utils.ContextUtils;
import org.graalvm.polyglot.Context; import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.PolyglotException; import org.graalvm.polyglot.PolyglotException;
@ -33,11 +32,7 @@ public class WarningsTest {
public static void initEnsoContext() { public static void initEnsoContext() {
ctx = ContextUtils.createDefaultContext(); ctx = ContextUtils.createDefaultContext();
generator = ValuesGenerator.create(ctx, ValuesGenerator.Language.ENSO); generator = ValuesGenerator.create(ctx, ValuesGenerator.Language.ENSO);
ensoContext = ensoContext = ContextUtils.leakContext(ctx);
(EnsoContext)
ctx.getBindings(LanguageInfo.ID)
.invokeMember(MethodNames.TopScope.LEAK_CONTEXT)
.asHostObject();
var module = var module =
ctx.eval( ctx.eval(
"enso", "enso",
@ -57,19 +52,22 @@ public class WarningsTest {
@Test @Test
public void doubleWithWarningsWrap() { public void doubleWithWarningsWrap() {
var warn1 = Warning.create(ensoContext, "w1", this); ContextUtils.executeInContext(
var warn2 = Warning.create(ensoContext, "w2", this); ctx,
var value = 42L; () -> {
var warn1 = Warning.create(ensoContext, "w1", this);
var warn2 = Warning.create(ensoContext, "w2", this);
var value = 42L;
var with1 = WithWarnings.wrap(ensoContext, value, warn1); var with1 = AppendWarningNode.getUncached().executeAppend(null, value, warn1);
var with2 = WithWarnings.wrap(ensoContext, with1, warn2); var with2 = AppendWarningNode.getUncached().executeAppend(null, with1, warn2);
assertEquals(value, with1.getValue()); assertEquals(value, with1.getValue());
assertEquals(value, with2.getValue()); assertEquals(value, with2.getValue());
Assert.assertArrayEquals( Assert.assertArrayEquals(new Object[] {warn1}, with1.getWarningsArray(false));
new Object[] {warn1}, with1.getWarningsArray(WarningsLibrary.getUncached(), false)); Assert.assertArrayEquals(new Object[] {warn1, warn2}, with2.getWarningsArray(false));
Assert.assertArrayEquals( return null;
new Object[] {warn1, warn2}, with2.getWarningsArray(WarningsLibrary.getUncached(), false)); });
} }
@Test @Test
@ -77,7 +75,7 @@ public class WarningsTest {
var value = 42; var value = 42;
WithWarnings without; WithWarnings without;
try { try {
without = WithWarnings.wrap(ensoContext, 42, new Warning[0]); without = AppendWarningNode.getUncached().executeAppend(null, 42, new Warning[0]);
} catch (AssertionError e) { } catch (AssertionError e) {
// OK // OK
return; return;

View File

@ -22,10 +22,9 @@ import org.enso.interpreter.runtime.data.atom.AtomConstructor;
import org.enso.interpreter.runtime.error.DataflowError; import org.enso.interpreter.runtime.error.DataflowError;
import org.enso.interpreter.runtime.error.PanicException; import org.enso.interpreter.runtime.error.PanicException;
import org.enso.interpreter.runtime.error.PanicSentinel; import org.enso.interpreter.runtime.error.PanicSentinel;
import org.enso.interpreter.runtime.error.Warning;
import org.enso.interpreter.runtime.error.WarningsLibrary;
import org.enso.interpreter.runtime.error.WithWarnings;
import org.enso.interpreter.runtime.state.State; import org.enso.interpreter.runtime.state.State;
import org.enso.interpreter.runtime.warning.AppendWarningNode;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
/** /**
* Invokes any callable with given arguments. * Invokes any callable with given arguments.
@ -74,7 +73,8 @@ public abstract class IndirectInvokeCallableNode extends Node {
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode, InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
BaseNode.TailStatus isTail, BaseNode.TailStatus isTail,
@Cached IndirectInvokeCallableNode invokeCallableNode, @Cached IndirectInvokeCallableNode invokeCallableNode,
@CachedLibrary(limit = "3") WarningsLibrary warnings) { @CachedLibrary(limit = "3") WarningsLibrary warnings,
@Cached AppendWarningNode appendWarningNode) {
try { try {
var result = var result =
invokeCallableNode.execute( invokeCallableNode.execute(
@ -87,8 +87,8 @@ public abstract class IndirectInvokeCallableNode extends Node {
argumentsExecutionMode, argumentsExecutionMode,
isTail); isTail);
Warning[] extracted = warnings.getWarnings(warning, null, false); var extracted = warnings.getWarnings(warning, false);
return WithWarnings.wrap(EnsoContext.get(this), result, extracted); return appendWarningNode.executeAppend(null, result, extracted);
} catch (UnsupportedMessageException e) { } catch (UnsupportedMessageException e) {
var ctx = EnsoContext.get(this); var ctx = EnsoContext.get(this);
throw ctx.raiseAssertionPanic(this, null, e); throw ctx.raiseAssertionPanic(this, null, e);

View File

@ -1,5 +1,6 @@
package org.enso.interpreter.node.callable; package org.enso.interpreter.node.callable;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Cached.Shared; import com.oracle.truffle.api.dsl.Cached.Shared;
import com.oracle.truffle.api.dsl.GenerateUncached; import com.oracle.truffle.api.dsl.GenerateUncached;
@ -19,15 +20,16 @@ import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.callable.UnresolvedConversion; import org.enso.interpreter.runtime.callable.UnresolvedConversion;
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo; import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
import org.enso.interpreter.runtime.callable.function.Function; import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.data.ArrayRope;
import org.enso.interpreter.runtime.data.Type; import org.enso.interpreter.runtime.data.Type;
import org.enso.interpreter.runtime.data.hash.EnsoHashMap;
import org.enso.interpreter.runtime.data.text.Text; import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.error.DataflowError; import org.enso.interpreter.runtime.error.DataflowError;
import org.enso.interpreter.runtime.error.PanicException; import org.enso.interpreter.runtime.error.PanicException;
import org.enso.interpreter.runtime.error.PanicSentinel; import org.enso.interpreter.runtime.error.PanicSentinel;
import org.enso.interpreter.runtime.error.Warning;
import org.enso.interpreter.runtime.error.WithWarnings;
import org.enso.interpreter.runtime.library.dispatch.TypeOfNode; import org.enso.interpreter.runtime.library.dispatch.TypeOfNode;
import org.enso.interpreter.runtime.warning.AppendWarningNode;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
import org.enso.interpreter.runtime.warning.WithWarnings;
@GenerateUncached @GenerateUncached
@ReportPolymorphism @ReportPolymorphism
@ -157,9 +159,16 @@ abstract class IndirectInvokeConversionNode extends Node {
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode, InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
BaseNode.TailStatus isTail, BaseNode.TailStatus isTail,
int thatArgumentPosition, int thatArgumentPosition,
@Cached IndirectInvokeConversionNode childDispatch) { @Cached IndirectInvokeConversionNode childDispatch,
@Cached AppendWarningNode appendWarningNode,
@CachedLibrary(limit = "3") WarningsLibrary warnsLib) {
arguments[thatArgumentPosition] = that.getValue(); arguments[thatArgumentPosition] = that.getValue();
ArrayRope<Warning> warnings = that.getReassignedWarningsAsRope(this, false); EnsoHashMap warnings;
try {
warnings = warnsLib.getWarnings(that, false);
} catch (UnsupportedMessageException e) {
throw CompilerDirectives.shouldNotReachHere(e);
}
Object result = Object result =
childDispatch.execute( childDispatch.execute(
frame, frame,
@ -173,7 +182,7 @@ abstract class IndirectInvokeConversionNode extends Node {
argumentsExecutionMode, argumentsExecutionMode,
isTail, isTail,
thatArgumentPosition); thatArgumentPosition);
return WithWarnings.appendTo(EnsoContext.get(this), result, warnings); return appendWarningNode.executeAppend(null, result, warnings);
} }
@Specialization(guards = "interop.isString(that)") @Specialization(guards = "interop.isString(that)")

View File

@ -1,5 +1,6 @@
package org.enso.interpreter.node.callable; package org.enso.interpreter.node.callable;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Bind;
import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Cached.Shared; import com.oracle.truffle.api.dsl.Cached.Shared;
@ -24,14 +25,15 @@ import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.callable.UnresolvedSymbol; import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo; import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
import org.enso.interpreter.runtime.callable.function.Function; import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.data.ArrayRope; import org.enso.interpreter.runtime.data.hash.EnsoHashMap;
import org.enso.interpreter.runtime.data.text.Text; import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.error.DataflowError; import org.enso.interpreter.runtime.error.DataflowError;
import org.enso.interpreter.runtime.error.PanicSentinel; import org.enso.interpreter.runtime.error.PanicSentinel;
import org.enso.interpreter.runtime.error.Warning;
import org.enso.interpreter.runtime.error.WithWarnings;
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary; import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
import org.enso.interpreter.runtime.state.State; import org.enso.interpreter.runtime.state.State;
import org.enso.interpreter.runtime.warning.AppendWarningNode;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
import org.enso.interpreter.runtime.warning.WithWarnings;
@GenerateUncached @GenerateUncached
@ReportPolymorphism @ReportPolymorphism
@ -129,9 +131,16 @@ public abstract class IndirectInvokeMethodNode extends Node {
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode, InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
BaseNode.TailStatus isTail, BaseNode.TailStatus isTail,
int thisArgumentPosition, int thisArgumentPosition,
@Cached IndirectInvokeMethodNode childDispatch) { @Cached IndirectInvokeMethodNode childDispatch,
@Cached AppendWarningNode appendWarningNode,
@CachedLibrary(limit = "3") WarningsLibrary warnsLib) {
arguments[thisArgumentPosition] = self.getValue(); arguments[thisArgumentPosition] = self.getValue();
ArrayRope<Warning> warnings = self.getReassignedWarningsAsRope(this, false); EnsoHashMap warnings;
try {
warnings = warnsLib.getWarnings(self, false);
} catch (UnsupportedMessageException e) {
throw CompilerDirectives.shouldNotReachHere(e);
}
Object result = Object result =
childDispatch.execute( childDispatch.execute(
frame, frame,
@ -144,7 +153,7 @@ public abstract class IndirectInvokeMethodNode extends Node {
argumentsExecutionMode, argumentsExecutionMode,
isTail, isTail,
thisArgumentPosition); thisArgumentPosition);
return WithWarnings.appendTo(EnsoContext.get(this), result, warnings); return appendWarningNode.executeAppend(null, result, warnings);
} }
@Specialization @Specialization

View File

@ -19,7 +19,6 @@ import java.util.UUID;
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Lock;
import org.enso.interpreter.Constants; import org.enso.interpreter.Constants;
import org.enso.interpreter.node.BaseNode; import org.enso.interpreter.node.BaseNode;
import org.enso.interpreter.node.BaseNode.TailStatus;
import org.enso.interpreter.node.callable.dispatch.InvokeFunctionNode; import org.enso.interpreter.node.callable.dispatch.InvokeFunctionNode;
import org.enso.interpreter.node.callable.thunk.ThunkExecutorNode; import org.enso.interpreter.node.callable.thunk.ThunkExecutorNode;
import org.enso.interpreter.runtime.EnsoContext; import org.enso.interpreter.runtime.EnsoContext;
@ -31,14 +30,14 @@ import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.control.TailCallException; import org.enso.interpreter.runtime.control.TailCallException;
import org.enso.interpreter.runtime.data.atom.Atom; import org.enso.interpreter.runtime.data.atom.Atom;
import org.enso.interpreter.runtime.data.atom.AtomConstructor; import org.enso.interpreter.runtime.data.atom.AtomConstructor;
import org.enso.interpreter.runtime.data.hash.EnsoHashMap;
import org.enso.interpreter.runtime.error.DataflowError; import org.enso.interpreter.runtime.error.DataflowError;
import org.enso.interpreter.runtime.error.PanicException; import org.enso.interpreter.runtime.error.PanicException;
import org.enso.interpreter.runtime.error.PanicSentinel; import org.enso.interpreter.runtime.error.PanicSentinel;
import org.enso.interpreter.runtime.error.Warning;
import org.enso.interpreter.runtime.error.WarningsLibrary;
import org.enso.interpreter.runtime.error.WithWarnings;
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary; import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
import org.enso.interpreter.runtime.state.State; import org.enso.interpreter.runtime.state.State;
import org.enso.interpreter.runtime.warning.AppendWarningNode;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
/** /**
* This class is responsible for performing the actual invocation of a given callable with its * This class is responsible for performing the actual invocation of a given callable with its
@ -287,12 +286,12 @@ public abstract class InvokeCallableNode extends BaseNode {
VirtualFrame callerFrame, VirtualFrame callerFrame,
State state, State state,
Object[] arguments, Object[] arguments,
@Shared("warnings") @CachedLibrary(limit = "3") WarningsLibrary warnings) { @Shared("warnings") @CachedLibrary(limit = "3") WarningsLibrary warnings,
@Cached AppendWarningNode appendWarningNode) {
Warning[] extracted; EnsoHashMap extracted;
Object callable; Object callable;
try { try {
extracted = warnings.getWarnings(warning, null, false); extracted = warnings.getWarnings(warning, false);
callable = warnings.removeWarnings(warning); callable = warnings.removeWarnings(warning);
} catch (UnsupportedMessageException e) { } catch (UnsupportedMessageException e) {
var ctx = EnsoContext.get(this); var ctx = EnsoContext.get(this);
@ -325,7 +324,7 @@ public abstract class InvokeCallableNode extends BaseNode {
if (result instanceof DataflowError) { if (result instanceof DataflowError) {
return result; return result;
} else { } else {
return WithWarnings.wrap(EnsoContext.get(this), result, extracted); return appendWarningNode.executeAppend(null, result, extracted);
} }
} catch (TailCallException e) { } catch (TailCallException e) {
throw new TailCallException(e, extracted); throw new TailCallException(e, extracted);

View File

@ -20,17 +20,18 @@ import org.enso.interpreter.runtime.callable.UnresolvedConversion;
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo; import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
import org.enso.interpreter.runtime.callable.function.Function; import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.control.TailCallException; import org.enso.interpreter.runtime.control.TailCallException;
import org.enso.interpreter.runtime.data.ArrayRope;
import org.enso.interpreter.runtime.data.EnsoMultiValue; 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.data.hash.EnsoHashMap;
import org.enso.interpreter.runtime.data.text.Text; import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.error.DataflowError; import org.enso.interpreter.runtime.error.DataflowError;
import org.enso.interpreter.runtime.error.PanicException; import org.enso.interpreter.runtime.error.PanicException;
import org.enso.interpreter.runtime.error.PanicSentinel; import org.enso.interpreter.runtime.error.PanicSentinel;
import org.enso.interpreter.runtime.error.Warning;
import org.enso.interpreter.runtime.error.WithWarnings;
import org.enso.interpreter.runtime.library.dispatch.TypeOfNode; import org.enso.interpreter.runtime.library.dispatch.TypeOfNode;
import org.enso.interpreter.runtime.state.State; import org.enso.interpreter.runtime.state.State;
import org.enso.interpreter.runtime.warning.AppendWarningNode;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
import org.enso.interpreter.runtime.warning.WithWarnings;
public abstract class InvokeConversionNode extends BaseNode { public abstract class InvokeConversionNode extends BaseNode {
private @Child InvokeFunctionNode invokeFunctionNode; private @Child InvokeFunctionNode invokeFunctionNode;
@ -198,7 +199,9 @@ public abstract class InvokeConversionNode extends BaseNode {
UnresolvedConversion conversion, UnresolvedConversion conversion,
Object self, Object self,
WithWarnings that, WithWarnings that,
Object[] arguments) { Object[] arguments,
@Cached AppendWarningNode appendWarningNode,
@CachedLibrary(limit = "3") WarningsLibrary warnsLib) {
// Cannot use @Cached for childDispatch, because we need to call notifyInserted. // Cannot use @Cached for childDispatch, because we need to call notifyInserted.
if (childDispatch == null) { if (childDispatch == null) {
CompilerDirectives.transferToInterpreterAndInvalidate(); CompilerDirectives.transferToInterpreterAndInvalidate();
@ -223,12 +226,17 @@ public abstract class InvokeConversionNode extends BaseNode {
} }
Object value = that.getValue(); Object value = that.getValue();
arguments[thatArgumentPosition] = value; arguments[thatArgumentPosition] = value;
ArrayRope<Warning> warnings = that.getReassignedWarningsAsRope(this, false); EnsoHashMap warnings;
try {
warnings = warnsLib.getWarnings(that, false);
} catch (UnsupportedMessageException e) {
throw CompilerDirectives.shouldNotReachHere(e);
}
try { try {
Object result = childDispatch.execute(frame, state, conversion, self, value, arguments); Object result = childDispatch.execute(frame, state, conversion, self, value, arguments);
return WithWarnings.appendTo(EnsoContext.get(this), result, warnings); return appendWarningNode.executeAppend(null, result, warnings);
} catch (TailCallException e) { } catch (TailCallException e) {
throw new TailCallException(e, warnings.toArray(Warning[]::new)); throw new TailCallException(e, warnings);
} }
} }

View File

@ -43,7 +43,6 @@ import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
import org.enso.interpreter.runtime.callable.function.Function; import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.callable.function.FunctionSchema; import org.enso.interpreter.runtime.callable.function.FunctionSchema;
import org.enso.interpreter.runtime.control.TailCallException; import org.enso.interpreter.runtime.control.TailCallException;
import org.enso.interpreter.runtime.data.ArrayRope;
import org.enso.interpreter.runtime.data.EnsoDate; import org.enso.interpreter.runtime.data.EnsoDate;
import org.enso.interpreter.runtime.data.EnsoDateTime; import org.enso.interpreter.runtime.data.EnsoDateTime;
import org.enso.interpreter.runtime.data.EnsoDuration; import org.enso.interpreter.runtime.data.EnsoDuration;
@ -51,15 +50,16 @@ import org.enso.interpreter.runtime.data.EnsoMultiValue;
import org.enso.interpreter.runtime.data.EnsoTimeOfDay; import org.enso.interpreter.runtime.data.EnsoTimeOfDay;
import org.enso.interpreter.runtime.data.EnsoTimeZone; import org.enso.interpreter.runtime.data.EnsoTimeZone;
import org.enso.interpreter.runtime.data.Type; import org.enso.interpreter.runtime.data.Type;
import org.enso.interpreter.runtime.data.hash.EnsoHashMap;
import org.enso.interpreter.runtime.data.hash.HashMapInsertAllNode;
import org.enso.interpreter.runtime.data.text.Text; import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.error.DataflowError; import org.enso.interpreter.runtime.error.DataflowError;
import org.enso.interpreter.runtime.error.PanicException; import org.enso.interpreter.runtime.error.PanicException;
import org.enso.interpreter.runtime.error.PanicSentinel; import org.enso.interpreter.runtime.error.PanicSentinel;
import org.enso.interpreter.runtime.error.Warning;
import org.enso.interpreter.runtime.error.WarningsLibrary;
import org.enso.interpreter.runtime.error.WithWarnings;
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary; import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
import org.enso.interpreter.runtime.state.State; import org.enso.interpreter.runtime.state.State;
import org.enso.interpreter.runtime.warning.AppendWarningNode;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
@ImportStatic({HostMethodCallNode.PolyglotCallType.class, HostMethodCallNode.class}) @ImportStatic({HostMethodCallNode.PolyglotCallType.class, HostMethodCallNode.class})
public abstract class InvokeMethodNode extends BaseNode { public abstract class InvokeMethodNode extends BaseNode {
@ -436,12 +436,13 @@ public abstract class InvokeMethodNode extends BaseNode {
UnresolvedSymbol symbol, UnresolvedSymbol symbol,
Object self, Object self,
Object[] arguments, Object[] arguments,
@Shared("warnings") @CachedLibrary(limit = "10") WarningsLibrary warnings) { @Shared("warnings") @CachedLibrary(limit = "10") WarningsLibrary warnings,
@Shared @Cached AppendWarningNode appendWarningNode) {
Object selfWithoutWarnings; Object selfWithoutWarnings;
Warning[] arrOfWarnings; EnsoHashMap warnsMap;
try { try {
selfWithoutWarnings = warnings.removeWarnings(self); selfWithoutWarnings = warnings.removeWarnings(self);
arrOfWarnings = warnings.getWarnings(self, this, false); warnsMap = warnings.getWarnings(self, false);
} catch (UnsupportedMessageException e) { } catch (UnsupportedMessageException e) {
var ctx = EnsoContext.get(this); var ctx = EnsoContext.get(this);
throw ctx.raiseAssertionPanic(this, null, e); throw ctx.raiseAssertionPanic(this, null, e);
@ -475,9 +476,10 @@ public abstract class InvokeMethodNode extends BaseNode {
try { try {
Object result = childDispatch.execute(frame, state, symbol, selfWithoutWarnings, arguments); Object result = childDispatch.execute(frame, state, symbol, selfWithoutWarnings, arguments);
return WithWarnings.appendTo(EnsoContext.get(this), result, arrOfWarnings); return appendWarningNode.executeAppend(null, result, warnsMap);
} catch (TailCallException e) { } catch (TailCallException e) {
throw new TailCallException(e, arrOfWarnings); CompilerDirectives.transferToInterpreter();
throw new TailCallException(e, warnsMap);
} }
} }
@ -505,10 +507,13 @@ public abstract class InvokeMethodNode extends BaseNode {
@Cached(value = "buildProfiles()", dimensions = 1) BranchProfile[] profiles, @Cached(value = "buildProfiles()", dimensions = 1) BranchProfile[] profiles,
@Cached(value = "buildProfiles()", dimensions = 1) BranchProfile[] warningProfiles, @Cached(value = "buildProfiles()", dimensions = 1) BranchProfile[] warningProfiles,
@Cached BranchProfile anyWarningsProfile, @Cached BranchProfile anyWarningsProfile,
@Cached HostMethodCallNode hostMethodCallNode) { @Cached HostMethodCallNode hostMethodCallNode,
@Shared @Cached AppendWarningNode appendWarningNode,
@Cached HashMapInsertAllNode mapInsertAllNode) {
Object[] args = new Object[argExecutors.length]; Object[] args = new Object[argExecutors.length];
boolean anyWarnings = false; boolean anyWarnings = false;
ArrayRope<Warning> accumulatedWarnings = new ArrayRope<>(); var accumulatedWarnings = EnsoHashMap.empty();
var maxWarnings = EnsoContext.get(this).getWarningsLimit();
for (int i = 0; i < argExecutors.length; i++) { for (int i = 0; i < argExecutors.length; i++) {
var r = argExecutors[i].executeThunk(frame, arguments[i + 1], state, TailStatus.NOT_TAIL); var r = argExecutors[i].executeThunk(frame, arguments[i + 1], state, TailStatus.NOT_TAIL);
if (r instanceof DataflowError) { if (r instanceof DataflowError) {
@ -518,7 +523,9 @@ public abstract class InvokeMethodNode extends BaseNode {
warningProfiles[i].enter(); warningProfiles[i].enter();
anyWarnings = true; anyWarnings = true;
try { try {
accumulatedWarnings = accumulatedWarnings.append(warnings.getWarnings(r, this, false)); EnsoHashMap rWarnsMap = warnings.getWarnings(r, false);
accumulatedWarnings =
mapInsertAllNode.executeInsertAll(frame, accumulatedWarnings, rWarnsMap, maxWarnings);
args[i] = warnings.removeWarnings(r); args[i] = warnings.removeWarnings(r);
} catch (UnsupportedMessageException e) { } catch (UnsupportedMessageException e) {
var ctx = EnsoContext.get(this); var ctx = EnsoContext.get(this);
@ -531,7 +538,7 @@ public abstract class InvokeMethodNode extends BaseNode {
Object res = hostMethodCallNode.execute(polyglotCallType, symbol.getName(), self, args); Object res = hostMethodCallNode.execute(polyglotCallType, symbol.getName(), self, args);
if (anyWarnings) { if (anyWarnings) {
anyWarningsProfile.enter(); anyWarningsProfile.enter();
res = WithWarnings.appendTo(EnsoContext.get(this), res, accumulatedWarnings); res = appendWarningNode.executeAppend(null, res, accumulatedWarnings);
} }
return res; return res;
} }

View File

@ -5,7 +5,7 @@ import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.api.nodes.NodeInfo;
import org.enso.interpreter.runtime.callable.CallerInfo; import org.enso.interpreter.runtime.callable.CallerInfo;
import org.enso.interpreter.runtime.callable.function.Function; import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.error.Warning; import org.enso.interpreter.runtime.data.hash.EnsoHashMap;
import org.enso.interpreter.runtime.state.State; import org.enso.interpreter.runtime.state.State;
/** /**
@ -43,5 +43,5 @@ public abstract class CallOptimiserNode extends Node {
CallerInfo callerInfo, CallerInfo callerInfo,
State state, State state,
Object[] arguments, Object[] arguments,
Warning[] warnings); EnsoHashMap warnings);
} }

View File

@ -17,13 +17,12 @@ import com.oracle.truffle.api.nodes.NodeInfo;
import com.oracle.truffle.api.nodes.RepeatingNode; import com.oracle.truffle.api.nodes.RepeatingNode;
import org.enso.interpreter.node.callable.ExecuteCallNode; import org.enso.interpreter.node.callable.ExecuteCallNode;
import org.enso.interpreter.node.callable.ExecuteCallNodeGen; import org.enso.interpreter.node.callable.ExecuteCallNodeGen;
import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.callable.CallerInfo; import org.enso.interpreter.runtime.callable.CallerInfo;
import org.enso.interpreter.runtime.callable.function.Function; import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.control.TailCallException; import org.enso.interpreter.runtime.control.TailCallException;
import org.enso.interpreter.runtime.error.Warning; import org.enso.interpreter.runtime.data.hash.EnsoHashMap;
import org.enso.interpreter.runtime.error.WithWarnings;
import org.enso.interpreter.runtime.state.State; import org.enso.interpreter.runtime.state.State;
import org.enso.interpreter.runtime.warning.AppendWarningNode;
/** /**
* A version of {@link CallOptimiserNode} that is fully prepared to handle tail calls. Tail calls * A version of {@link CallOptimiserNode} that is fully prepared to handle tail calls. Tail calls
@ -65,7 +64,7 @@ public abstract class LoopingCallOptimiserNode extends CallOptimiserNode {
CallerInfo callerInfo, CallerInfo callerInfo,
State state, State state,
Object[] arguments, Object[] arguments,
Warning[] warnings, EnsoHashMap warnings,
@Shared("loopNode") @Cached(value = "createLoopNode()") LoopNode loopNode) { @Shared("loopNode") @Cached(value = "createLoopNode()") LoopNode loopNode) {
return dispatch(function, callerInfo, state, arguments, loopNode); return dispatch(function, callerInfo, state, arguments, loopNode);
} }
@ -76,10 +75,11 @@ public abstract class LoopingCallOptimiserNode extends CallOptimiserNode {
CallerInfo callerInfo, CallerInfo callerInfo,
State state, State state,
Object[] arguments, Object[] arguments,
Warning[] warnings, EnsoHashMap warnings,
@Shared("loopNode") @Cached(value = "createLoopNode()") LoopNode loopNode) { @Shared("loopNode") @Cached(value = "createLoopNode()") LoopNode loopNode,
@Shared @Cached AppendWarningNode appendWarningNode) {
Object result = dispatch(function, callerInfo, state, arguments, loopNode); Object result = dispatch(function, callerInfo, state, arguments, loopNode);
return WithWarnings.appendTo(EnsoContext.get(this), result, warnings); return appendWarningNode.executeAppend(null, result, warnings);
} }
private Object dispatch( private Object dispatch(
@ -104,7 +104,7 @@ public abstract class LoopingCallOptimiserNode extends CallOptimiserNode {
CallerInfo callerInfo, CallerInfo callerInfo,
State state, State state,
Object[] arguments, Object[] arguments,
Warning[] warnings, EnsoHashMap warnings,
@Shared("executeCallNode") @Cached ExecuteCallNode executeCallNode) { @Shared("executeCallNode") @Cached ExecuteCallNode executeCallNode) {
return loopUntilCompletion(frame, function, callerInfo, state, arguments, executeCallNode); return loopUntilCompletion(frame, function, callerInfo, state, arguments, executeCallNode);
} }
@ -117,11 +117,12 @@ public abstract class LoopingCallOptimiserNode extends CallOptimiserNode {
CallerInfo callerInfo, CallerInfo callerInfo,
State state, State state,
Object[] arguments, Object[] arguments,
Warning[] warnings, EnsoHashMap warnings,
@Shared("executeCallNode") @Cached ExecuteCallNode executeCallNode) { @Shared("executeCallNode") @Cached ExecuteCallNode executeCallNode,
@Shared @Cached AppendWarningNode appendWarningNode) {
Object result = Object result =
loopUntilCompletion(frame, function, callerInfo, state, arguments, executeCallNode); loopUntilCompletion(frame, function, callerInfo, state, arguments, executeCallNode);
return WithWarnings.appendTo(EnsoContext.get(this), result, warnings); return appendWarningNode.executeAppend(null, result, warnings);
} }
private Object loopUntilCompletion( private Object loopUntilCompletion(

View File

@ -9,7 +9,7 @@ import org.enso.interpreter.node.callable.ExecuteCallNodeGen;
import org.enso.interpreter.runtime.callable.CallerInfo; import org.enso.interpreter.runtime.callable.CallerInfo;
import org.enso.interpreter.runtime.callable.function.Function; import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.control.TailCallException; import org.enso.interpreter.runtime.control.TailCallException;
import org.enso.interpreter.runtime.error.Warning; import org.enso.interpreter.runtime.data.hash.EnsoHashMap;
import org.enso.interpreter.runtime.state.State; import org.enso.interpreter.runtime.state.State;
/** /**
@ -51,7 +51,7 @@ public class SimpleCallOptimiserNode extends CallOptimiserNode {
CallerInfo callerInfo, CallerInfo callerInfo,
State state, State state,
Object[] arguments, Object[] arguments,
Warning[] warnings) { EnsoHashMap warnings) {
try { try {
return executeCallNode.executeCall(frame, function, callerInfo, state, arguments); return executeCallNode.executeCall(frame, function, callerInfo, state, arguments);
} catch (TailCallException e) { } catch (TailCallException e) {

View File

@ -1,6 +1,7 @@
package org.enso.interpreter.node.controlflow.caseexpr; package org.enso.interpreter.node.controlflow.caseexpr;
import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Cached.Shared; import com.oracle.truffle.api.dsl.Cached.Shared;
import com.oracle.truffle.api.dsl.NodeChild; import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.dsl.Specialization;
@ -16,6 +17,8 @@ import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.error.*; import org.enso.interpreter.runtime.error.*;
import org.enso.interpreter.runtime.state.State; import org.enso.interpreter.runtime.state.State;
import org.enso.interpreter.runtime.type.TypesGen; import org.enso.interpreter.runtime.type.TypesGen;
import org.enso.interpreter.runtime.warning.AppendWarningNode;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
/** /**
* A node representing a pattern match on an arbitrary runtime value. * A node representing a pattern match on an arbitrary runtime value.
@ -82,12 +85,12 @@ public abstract class CaseNode extends ExpressionNode {
Object doWarning( Object doWarning(
VirtualFrame frame, VirtualFrame frame,
Object object, Object object,
@Shared("warnsLib") @CachedLibrary(limit = "3") WarningsLibrary warnings) { @Shared("warnsLib") @CachedLibrary(limit = "3") WarningsLibrary warnings,
@Cached AppendWarningNode appendWarningNode) {
try { try {
EnsoContext ctx = EnsoContext.get(this); var ws = warnings.getWarnings(object, false);
Warning[] ws = warnings.getWarnings(object, this, false);
Object result = doMatch(frame, warnings.removeWarnings(object), warnings); Object result = doMatch(frame, warnings.removeWarnings(object), warnings);
return WithWarnings.wrap(ctx, result, ws); return appendWarningNode.executeAppend(null, result, ws);
} catch (UnsupportedMessageException e) { } catch (UnsupportedMessageException e) {
throw EnsoContext.get(this).raiseAssertionPanic(this, null, e); throw EnsoContext.get(this).raiseAssertionPanic(this, null, e);
} }

View File

@ -13,8 +13,8 @@ import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.profiles.CountingConditionProfile; import com.oracle.truffle.api.profiles.CountingConditionProfile;
import org.enso.interpreter.runtime.EnsoContext; import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.data.text.Text; import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.error.WarningsLibrary; import org.enso.interpreter.runtime.warning.AppendWarningNode;
import org.enso.interpreter.runtime.error.WithWarnings; import org.enso.interpreter.runtime.warning.WarningsLibrary;
/** /**
* Converts a value returned by a polyglot call back to a value that can be further used within Enso * Converts a value returned by a polyglot call back to a value that can be further used within Enso
@ -75,13 +75,14 @@ public abstract class HostValueToEnsoNode extends Node {
Object value, Object value,
@CachedLibrary(limit = "3") InteropLibrary iop, @CachedLibrary(limit = "3") InteropLibrary iop,
@CachedLibrary(limit = "3") WarningsLibrary warningsLibrary, @CachedLibrary(limit = "3") WarningsLibrary warningsLibrary,
@Cached CountingConditionProfile nullWarningProfile) { @Cached CountingConditionProfile nullWarningProfile,
@Cached AppendWarningNode appendWarningNode) {
var ctx = EnsoContext.get(this); var ctx = EnsoContext.get(this);
var nothing = ctx.getBuiltins().nothing(); var nothing = ctx.getBuiltins().nothing();
if (nothing != value && nullWarningProfile.profile(warningsLibrary.hasWarnings(value))) { if (nothing != value && nullWarningProfile.profile(warningsLibrary.hasWarnings(value))) {
try { try {
var attachedWarnings = warningsLibrary.getWarnings(value, null, false); var attachedWarnings = warningsLibrary.getWarnings(value, false);
return WithWarnings.wrap(ctx, nothing, attachedWarnings); return appendWarningNode.executeAppend(null, nothing, attachedWarnings);
} catch (UnsupportedMessageException e) { } catch (UnsupportedMessageException e) {
return nothing; return nothing;
} }

View File

@ -17,8 +17,8 @@ import org.enso.interpreter.node.callable.InvokeCallableNode;
import org.enso.interpreter.runtime.EnsoContext; import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.callable.UnresolvedSymbol; import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo; import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
import org.enso.interpreter.runtime.error.WarningsLibrary;
import org.enso.interpreter.runtime.state.State; import org.enso.interpreter.runtime.state.State;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
@BuiltinMethod( @BuiltinMethod(
type = "IO", type = "IO",

View File

@ -27,9 +27,9 @@ import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
import org.enso.interpreter.runtime.data.EnsoFile; import org.enso.interpreter.runtime.data.EnsoFile;
import org.enso.interpreter.runtime.data.Type; import org.enso.interpreter.runtime.data.Type;
import org.enso.interpreter.runtime.data.atom.Atom; import org.enso.interpreter.runtime.data.atom.Atom;
import org.enso.interpreter.runtime.error.WarningsLibrary;
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary; import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
import org.enso.interpreter.runtime.scope.ModuleScope; import org.enso.interpreter.runtime.scope.ModuleScope;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
@GenerateUncached @GenerateUncached
public abstract class EqualsComplexNode extends Node { public abstract class EqualsComplexNode extends Node {

View File

@ -21,8 +21,8 @@ import org.enso.interpreter.runtime.data.EnsoObject;
import org.enso.interpreter.runtime.data.atom.Atom; import org.enso.interpreter.runtime.data.atom.Atom;
import org.enso.interpreter.runtime.data.atom.AtomConstructor; import org.enso.interpreter.runtime.data.atom.AtomConstructor;
import org.enso.interpreter.runtime.data.text.Text; import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.error.WarningsLibrary;
import org.enso.interpreter.runtime.number.EnsoBigInteger; import org.enso.interpreter.runtime.number.EnsoBigInteger;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
import org.enso.polyglot.common_utils.Core_Text_Utils; import org.enso.polyglot.common_utils.Core_Text_Utils;
@GenerateUncached @GenerateUncached

View File

@ -45,11 +45,11 @@ import org.enso.interpreter.runtime.data.atom.Atom;
import org.enso.interpreter.runtime.data.atom.AtomConstructor; import org.enso.interpreter.runtime.data.atom.AtomConstructor;
import org.enso.interpreter.runtime.data.atom.StructsLibrary; import org.enso.interpreter.runtime.data.atom.StructsLibrary;
import org.enso.interpreter.runtime.data.text.Text; import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.error.WarningsLibrary;
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.scope.ModuleScope; import org.enso.interpreter.runtime.scope.ModuleScope;
import org.enso.interpreter.runtime.state.State; import org.enso.interpreter.runtime.state.State;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
import org.enso.polyglot.common_utils.Core_Text_Utils; import org.enso.polyglot.common_utils.Core_Text_Utils;
/** /**

View File

@ -21,8 +21,8 @@ import org.enso.interpreter.node.expression.builtin.number.utils.ToEnsoNumberNod
import org.enso.interpreter.runtime.EnsoContext; import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.data.EnsoObject; import org.enso.interpreter.runtime.data.EnsoObject;
import org.enso.interpreter.runtime.data.text.Text; import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.error.WarningsLibrary;
import org.enso.interpreter.runtime.number.EnsoBigInteger; import org.enso.interpreter.runtime.number.EnsoBigInteger;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
@BuiltinMethod( @BuiltinMethod(
type = "Default_Comparator", type = "Default_Comparator",

View File

@ -39,12 +39,13 @@ import org.enso.interpreter.runtime.data.vector.ArrayLikeHelpers;
import org.enso.interpreter.runtime.data.vector.ArrayLikeLengthNode; import org.enso.interpreter.runtime.data.vector.ArrayLikeLengthNode;
import org.enso.interpreter.runtime.error.DataflowError; import org.enso.interpreter.runtime.error.DataflowError;
import org.enso.interpreter.runtime.error.PanicException; import org.enso.interpreter.runtime.error.PanicException;
import org.enso.interpreter.runtime.error.Warning;
import org.enso.interpreter.runtime.error.WarningsLibrary;
import org.enso.interpreter.runtime.error.WithWarnings;
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.state.State; import org.enso.interpreter.runtime.state.State;
import org.enso.interpreter.runtime.warning.AppendWarningNode;
import org.enso.interpreter.runtime.warning.Warning;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
import org.enso.interpreter.runtime.warning.WithWarnings;
/** /**
* Sorts a vector with elements that have only Default_Comparator, thus, only elements with a * Sorts a vector with elements that have only Default_Comparator, thus, only elements with a
@ -66,7 +67,7 @@ public abstract class SortVectorNode extends Node {
* @param self Vector that has elements with only Default_Comparator, that are elements with * @param self Vector that has elements with only Default_Comparator, that are elements with
* builtin types. * builtin types.
* @param ascending -1 for descending, 1 for ascending * @param ascending -1 for descending, 1 for ascending
* @param comparators Vector of comparators, with the same length of self. This is gather in the * @param comparators Vector of comparators, with the same length of self. This is gathered in the
* Enso code, because doing that in this builtin would be difficult. If {@code onFunc} * Enso code, because doing that in this builtin would be difficult. If {@code onFunc}
* parameter is not {@code Nothing}, comparators are gathered from the result of {@code * parameter is not {@code Nothing}, comparators are gathered from the result of {@code
* onFunc} projection. * onFunc} projection.
@ -354,7 +355,8 @@ public abstract class SortVectorNode extends Node {
.map(text -> Warning.create(ctx, text, this)) .map(text -> Warning.create(ctx, text, this))
.limit(MAX_SORT_WARNINGS) .limit(MAX_SORT_WARNINGS)
.toArray(Warning[]::new); .toArray(Warning[]::new);
return WithWarnings.appendTo(ctx, vector, warnArray.length < warnings.size(), warnArray); var reachedMaxCount = warnArray.length < warnings.size();
return WithWarnings.create(vector, MAX_SORT_WARNINGS, reachedMaxCount, warnArray);
} }
private Object attachDifferentComparatorsWarning(Object vector, List<Group> groups) { private Object attachDifferentComparatorsWarning(Object vector, List<Group> groups) {
@ -366,8 +368,13 @@ public abstract class SortVectorNode extends Node {
.collect(Collectors.joining(", ")); .collect(Collectors.joining(", "));
var text = Text.create("Different comparators: [" + diffCompsMsg + "]"); var text = Text.create("Different comparators: [" + diffCompsMsg + "]");
var ctx = EnsoContext.get(this); var ctx = EnsoContext.get(this);
var warn = Warning.create(ctx, text, this); var warnsLib = WarningsLibrary.getUncached();
return WithWarnings.appendTo(ctx, vector, false, warn); if (warnsLib.hasWarnings(vector) && warnsLib.isLimitReached(vector)) {
return vector;
} else {
var warn = Warning.create(ctx, text, this);
return AppendWarningNode.getUncached().executeAppend(null, vector, warn);
}
} else { } else {
return vector; return vector;
} }

View File

@ -13,7 +13,7 @@ import org.enso.interpreter.runtime.builtin.Builtins;
import org.enso.interpreter.runtime.data.atom.Atom; import org.enso.interpreter.runtime.data.atom.Atom;
import org.enso.interpreter.runtime.data.text.Text; import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.error.PanicException; import org.enso.interpreter.runtime.error.PanicException;
import org.enso.interpreter.runtime.error.WarningsLibrary; import org.enso.interpreter.runtime.warning.WarningsLibrary;
public abstract class ExpectStringNode extends Node { public abstract class ExpectStringNode extends Node {
private @Child InteropLibrary library = InteropLibrary.getFactory().createDispatched(10); private @Child InteropLibrary library = InteropLibrary.getFactory().createDispatched(10);

View File

@ -3,7 +3,7 @@ package org.enso.interpreter.runtime.control;
import com.oracle.truffle.api.nodes.ControlFlowException; import com.oracle.truffle.api.nodes.ControlFlowException;
import org.enso.interpreter.runtime.callable.CallerInfo; import org.enso.interpreter.runtime.callable.CallerInfo;
import org.enso.interpreter.runtime.callable.function.Function; import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.error.Warning; import org.enso.interpreter.runtime.data.hash.EnsoHashMap;
/** /**
* Used to model the switch of control-flow from standard stack-based execution to looping. * Used to model the switch of control-flow from standard stack-based execution to looping.
@ -14,7 +14,13 @@ public class TailCallException extends ControlFlowException {
private final Function function; private final Function function;
private final CallerInfo callerInfo; private final CallerInfo callerInfo;
private final Object[] arguments; private final Object[] arguments;
private final Warning[] warnings;
/**
* May be null.
*
* @see org.enso.interpreter.runtime.warning.WithWarnings#warnings
*/
private final EnsoHashMap warnings;
/** /**
* Creates a new exception containing the necessary data to continue computation. * Creates a new exception containing the necessary data to continue computation.
@ -31,7 +37,7 @@ public class TailCallException extends ControlFlowException {
} }
private TailCallException( private TailCallException(
Function function, CallerInfo callerInfo, Object[] arguments, Warning[] warnings) { Function function, CallerInfo callerInfo, Object[] arguments, EnsoHashMap warnings) {
this.function = function; this.function = function;
this.callerInfo = callerInfo; this.callerInfo = callerInfo;
this.arguments = arguments; this.arguments = arguments;
@ -44,7 +50,7 @@ public class TailCallException extends ControlFlowException {
* @param origin the original tail call exception * @param origin the original tail call exception
* @param warnings warnings to be associated with the tail call exception * @param warnings warnings to be associated with the tail call exception
*/ */
public TailCallException(TailCallException origin, Warning[] warnings) { public TailCallException(TailCallException origin, EnsoHashMap warnings) {
this(origin.getFunction(), origin.getCallerInfo(), origin.getArguments(), warnings); this(origin.getFunction(), origin.getCallerInfo(), origin.getArguments(), warnings);
} }
@ -80,7 +86,7 @@ public class TailCallException extends ControlFlowException {
* *
* @return the warnings to be appended to the result of the call, or null if empty * @return the warnings to be appended to the result of the call, or null if empty
*/ */
public Warning[] getWarnings() { public EnsoHashMap getWarnings() {
return warnings; return warnings;
} }
} }

View File

@ -1,123 +0,0 @@
package org.enso.interpreter.runtime.data;
import com.oracle.truffle.api.CompilerDirectives;
import java.util.Arrays;
import java.util.function.Function;
public final class ArrayRope<T> {
private final ArrayRopeSegment<T> segment;
private ArrayRope(ArrayRopeSegment<T> segment) {
this.segment = segment;
}
@SafeVarargs
public ArrayRope(T... elements) {
segment = new ArraySegment<>(elements);
}
public ArrayRope<T> append(ArrayRope<T> that) {
return new ArrayRope<>(new ConcatSegment<>(this.segment, that.segment));
}
@SafeVarargs
public final ArrayRope<T> append(T... items) {
return new ArrayRope<>(new ConcatSegment<>(this.segment, new ArraySegment<>(items)));
}
public ArrayRope<T> prepend(ArrayRope<T> that) {
return new ArrayRope<>(new ConcatSegment<>(that.segment, this.segment));
}
@SafeVarargs
public final ArrayRope<T> prepend(T... items) {
return new ArrayRope<>(new ConcatSegment<>(new ArraySegment<>(items), this.segment));
}
@CompilerDirectives.TruffleBoundary
public T[] toArray(Function<Integer, T[]> genArray) {
T[] res = genArray.apply(size());
writeArray(res);
return res;
}
public void writeArray(T[] arr) {
segment.appendTo(arr, 0);
}
public int size() {
return segment.size();
}
@Override
public String toString() {
return "ArrayRope{" + "segment=" + segment + '}';
}
private interface ArrayRopeSegment<T> {
void appendTo(T[] builder, int start);
int size();
}
private static final class ArraySegment<T> implements ArrayRopeSegment<T> {
private final T[] elements;
public ArraySegment(T[] elements) {
this.elements = elements;
}
@Override
public void appendTo(T[] builder, int start) {
System.arraycopy(elements, 0, builder, start, elements.length);
}
@Override
public int size() {
return elements.length;
}
@Override
public String toString() {
return "ArraySegment{" + "elements=" + Arrays.toString(elements) + '}';
}
}
private static final class ConcatSegment<T> implements ArrayRopeSegment<T> {
private final ArrayRopeSegment<T> left;
private final ArrayRopeSegment<T> right;
private int cachedSize = UNKNOWN;
private static final int UNKNOWN = -1;
public ConcatSegment(ArrayRopeSegment<T> left, ArrayRopeSegment<T> right) {
this.left = left;
this.right = right;
}
@Override
public void appendTo(T[] builder, int start) {
left.appendTo(builder, start);
right.appendTo(builder, start + left.size());
}
@Override
public int size() {
if (cachedSize == UNKNOWN) {
cachedSize = left.size() + right.size();
}
return cachedSize;
}
@Override
public String toString() {
return "ConcatSegment{"
+ "left="
+ left
+ ", right="
+ right
+ ", cachedSize="
+ cachedSize
+ '}';
}
}
}

View File

@ -26,9 +26,9 @@ import org.enso.interpreter.runtime.data.Type;
import org.enso.interpreter.runtime.data.text.Text; import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.data.vector.ArrayLikeHelpers; import org.enso.interpreter.runtime.data.vector.ArrayLikeHelpers;
import org.enso.interpreter.runtime.error.PanicException; import org.enso.interpreter.runtime.error.PanicException;
import org.enso.interpreter.runtime.error.WarningsLibrary;
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary; import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
import org.enso.interpreter.runtime.type.TypesGen; import org.enso.interpreter.runtime.type.TypesGen;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
/** A runtime representation of an Atom in Enso. */ /** A runtime representation of an Atom in Enso. */
@ExportLibrary(InteropLibrary.class) @ExportLibrary(InteropLibrary.class)

View File

@ -11,11 +11,11 @@ import com.oracle.truffle.api.profiles.BranchProfile;
import com.oracle.truffle.api.profiles.CountingConditionProfile; import com.oracle.truffle.api.profiles.CountingConditionProfile;
import org.enso.interpreter.node.ExpressionNode; import org.enso.interpreter.node.ExpressionNode;
import org.enso.interpreter.runtime.EnsoContext; import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.data.ArrayRope; import org.enso.interpreter.runtime.data.hash.EnsoHashMap;
import org.enso.interpreter.runtime.error.Warning; import org.enso.interpreter.runtime.data.hash.HashMapInsertAllNode;
import org.enso.interpreter.runtime.error.WarningsLibrary;
import org.enso.interpreter.runtime.error.WithWarnings;
import org.enso.interpreter.runtime.type.TypesGen; import org.enso.interpreter.runtime.type.TypesGen;
import org.enso.interpreter.runtime.warning.AppendWarningNode;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
/** /**
* A node instantiating a constant {@link AtomConstructor} with values computed based on the * A node instantiating a constant {@link AtomConstructor} with values computed based on the
@ -66,10 +66,13 @@ abstract class InstantiateNode extends ExpressionNode {
@ExplodeLoop @ExplodeLoop
Object doExecute( Object doExecute(
VirtualFrame frame, VirtualFrame frame,
@Cached(parameters = {"constructor"}) AtomConstructorInstanceNode createInstanceNode) { @Cached(parameters = {"constructor"}) AtomConstructorInstanceNode createInstanceNode,
@Cached AppendWarningNode appendWarningNode,
@Cached HashMapInsertAllNode mapInsertAllNode) {
Object[] argumentValues = new Object[arguments.length]; Object[] argumentValues = new Object[arguments.length];
boolean anyWarnings = false; boolean anyWarnings = false;
ArrayRope<Warning> accumulatedWarnings = new ArrayRope<>(); var accumulatedWarnings = EnsoHashMap.empty();
var maxWarnings = EnsoContext.get(this).getWarningsLimit();
for (int i = 0; i < arguments.length; i++) { for (int i = 0; i < arguments.length; i++) {
CountingConditionProfile profile = profiles[i]; CountingConditionProfile profile = profiles[i];
CountingConditionProfile warningProfile = warningProfiles[i]; CountingConditionProfile warningProfile = warningProfiles[i];
@ -80,8 +83,10 @@ abstract class InstantiateNode extends ExpressionNode {
} else if (warningProfile.profile(warnings.hasWarnings(argument))) { } else if (warningProfile.profile(warnings.hasWarnings(argument))) {
anyWarnings = true; anyWarnings = true;
try { try {
var argumentWarnsMap = warnings.getWarnings(argument, false);
accumulatedWarnings = accumulatedWarnings =
accumulatedWarnings.append(warnings.getWarnings(argument, this, false)); mapInsertAllNode.executeInsertAll(
frame, accumulatedWarnings, argumentWarnsMap, maxWarnings);
argumentValues[i] = warnings.removeWarnings(argument); argumentValues[i] = warnings.removeWarnings(argument);
} catch (UnsupportedMessageException e) { } catch (UnsupportedMessageException e) {
throw EnsoContext.get(this).raiseAssertionPanic(this, null, e); throw EnsoContext.get(this).raiseAssertionPanic(this, null, e);
@ -94,8 +99,8 @@ abstract class InstantiateNode extends ExpressionNode {
} }
} }
if (anyWarningsProfile.profile(anyWarnings)) { if (anyWarningsProfile.profile(anyWarnings)) {
return WithWarnings.appendTo( return appendWarningNode.executeAppend(
EnsoContext.get(this), createInstanceNode.execute(argumentValues), accumulatedWarnings); frame, createInstanceNode.execute(argumentValues), accumulatedWarnings);
} else { } else {
return createInstanceNode.execute(argumentValues); return createInstanceNode.execute(argumentValues);
} }

View File

@ -66,6 +66,7 @@ public final class EnsoHashMap implements EnsoObject {
} }
} }
/** Slow version of {@link #getCachedVectorRepresentation(ConditionProfile)}. */
Object getCachedVectorRepresentation() { Object getCachedVectorRepresentation() {
return getCachedVectorRepresentation(ConditionProfile.getUncached()); return getCachedVectorRepresentation(ConditionProfile.getUncached());
} }

View File

@ -3,6 +3,7 @@ package org.enso.interpreter.runtime.data.hash;
import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.frame.VirtualFrame;
import java.util.Arrays; import java.util.Arrays;
import java.util.Iterator;
import org.enso.interpreter.node.expression.builtin.meta.EqualsNode; import org.enso.interpreter.node.expression.builtin.meta.EqualsNode;
import org.enso.interpreter.node.expression.builtin.meta.HashCodeNode; import org.enso.interpreter.node.expression.builtin.meta.HashCodeNode;
@ -48,17 +49,22 @@ final class EnsoHashMapBuilder {
} }
/** Create a new builder with default size being {@code 11}. */ /** Create a new builder with default size being {@code 11}. */
public static EnsoHashMapBuilder create() { static EnsoHashMapBuilder create() {
return new EnsoHashMapBuilder(11); return new EnsoHashMapBuilder(11);
} }
static EnsoHashMapBuilder createWithCapacity(int capacity) {
assert capacity > 0;
return new EnsoHashMapBuilder(capacity);
}
/** Returns count of elements in the storage. */ /** Returns count of elements in the storage. */
public int generation() { int generation() {
return generation; return generation;
} }
/** Returns the actual number of visible elements in current generation. */ /** Returns the actual number of visible elements in current generation. */
public int size() { int size() {
return actualSize; return actualSize;
} }
@ -66,7 +72,7 @@ final class EnsoHashMapBuilder {
* Provides access to all {@code StorageEntry} in this builder at given {@code atGeneration}. * Provides access to all {@code StorageEntry} in this builder at given {@code atGeneration}.
* Classical usage is to {@code for (var e : this) if (e.isVisible(atGeneration) operation(e))}. * Classical usage is to {@code for (var e : this) if (e.isVisible(atGeneration) operation(e))}.
*/ */
public StorageEntry[] getEntries(int atGeneration, int size) { StorageEntry[] getEntries(int atGeneration, int size) {
var arr = new StorageEntry[size]; var arr = new StorageEntry[size];
var at = 0; var at = 0;
for (var i = 0; i < byHash.length; i++) { for (var i = 0; i < byHash.length; i++) {
@ -82,12 +88,57 @@ final class EnsoHashMapBuilder {
} }
} }
record Entry(Object key, Object value) {}
Iterator<Entry> getEntriesIterator(int atGeneration) {
return new EntriesIterator(atGeneration);
}
final class EntriesIterator implements Iterator<Entry> {
private final int atGeneration;
private int nextVisibleEntryIdx = -1;
EntriesIterator(int atGeneration) {
this.atGeneration = atGeneration;
skipToNextVisibleEntry();
}
@Override
public boolean hasNext() {
if (nextVisibleEntryIdx >= byHash.length) {
return false;
}
var entry = byHash[nextVisibleEntryIdx];
return entry != null && entry.isVisible(atGeneration);
}
@Override
public Entry next() {
var entry = byHash[nextVisibleEntryIdx];
skipToNextVisibleEntry();
return new Entry(entry.key(), entry.value());
}
private void skipToNextVisibleEntry() {
while (true) {
nextVisibleEntryIdx++;
if (nextVisibleEntryIdx == byHash.length) {
break;
}
var entry = byHash[nextVisibleEntryIdx];
if (entry != null && entry.isVisible(atGeneration)) {
break;
}
}
}
}
/** /**
* Prepares a builder ready for modification at given generation. It may return {@code this} if * Prepares a builder ready for modification at given generation. It may return {@code this} if
* the {@code atGeneration == this.generation} and the {@code byHash} array is less than 75% full. * the {@code atGeneration == this.generation} and the {@code byHash} array is less than 75% full.
* Otherwise it may return new builder suitable for additions. * Otherwise it may return new builder suitable for additions.
*/ */
public EnsoHashMapBuilder asModifiable( EnsoHashMapBuilder asModifiable(
VirtualFrame frame, int atGeneration, HashCodeNode hashCodeNode, EqualsNode equalsNode) { VirtualFrame frame, int atGeneration, HashCodeNode hashCodeNode, EqualsNode equalsNode) {
if (atGeneration != generation || generation * 4 > byHash.length * 3) { if (atGeneration != generation || generation * 4 > byHash.length * 3) {
var newSize = Math.max(actualSize * 2, byHash.length); var newSize = Math.max(actualSize * 2, byHash.length);
@ -103,7 +154,7 @@ final class EnsoHashMapBuilder {
* equal key, it marks it as removed, if it hasn't been removed yet. Once it finds an empty slot, * equal key, it marks it as removed, if it hasn't been removed yet. Once it finds an empty slot,
* it puts there a new entry with the next generation. * it puts there a new entry with the next generation.
*/ */
public void put( void put(
VirtualFrame frame, VirtualFrame frame,
Object key, Object key,
Object value, Object value,
@ -139,7 +190,7 @@ final class EnsoHashMapBuilder {
* Finds storage entry for given key or {@code null}. Searches only entries that are visible for * Finds storage entry for given key or {@code null}. Searches only entries that are visible for
* given {@code generation}. * given {@code generation}.
*/ */
public StorageEntry get( StorageEntry get(
VirtualFrame frame, VirtualFrame frame,
Object key, Object key,
int generation, int generation,
@ -175,8 +226,7 @@ final class EnsoHashMapBuilder {
* *
* @return true if the removal was successful false otherwise. * @return true if the removal was successful false otherwise.
*/ */
public boolean remove( boolean remove(VirtualFrame frame, Object key, HashCodeNode hashCodeNode, EqualsNode equalsNode) {
VirtualFrame frame, Object key, HashCodeNode hashCodeNode, EqualsNode equalsNode) {
assert actualSize <= generation; assert actualSize <= generation;
var at = findWhereToStart(key, hashCodeNode); var at = findWhereToStart(key, hashCodeNode);
var nextGeneration = ++generation; var nextGeneration = ++generation;
@ -225,11 +275,11 @@ final class EnsoHashMapBuilder {
* are in the storage as of this moment, i.e., all the entries with their indexes lesser than * are in the storage as of this moment, i.e., all the entries with their indexes lesser than
* {@code generation}. * {@code generation}.
* *
* <p>Should be called where most once for a particular {@code generation}. * <p>Should be called at most once for a particular {@code generation}.
* *
* @return A new hash map snapshot. * @return A new hash map snapshot.
*/ */
public EnsoHashMap build() { EnsoHashMap build() {
return EnsoHashMap.createWithBuilder(this); return EnsoHashMap.createWithBuilder(this);
} }

View File

@ -0,0 +1,73 @@
package org.enso.interpreter.runtime.data.hash;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.GenerateUncached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.NodeInfo;
import org.enso.common.LanguageInfo;
import org.enso.interpreter.node.expression.builtin.meta.EqualsNode;
import org.enso.interpreter.node.expression.builtin.meta.HashCodeNode;
@GenerateUncached
@NodeInfo(
shortName = "MapInsertAll",
description = "Inserts all elements from the given container into a hash map",
language = LanguageInfo.ID)
public abstract class HashMapInsertAllNode extends Node {
public static HashMapInsertAllNode build() {
return HashMapInsertAllNodeGen.create();
}
public static HashMapInsertAllNode getUncached() {
return HashMapInsertAllNodeGen.getUncached();
}
/**
* Insert all the elements from the given container into the given map.
*
* @param self A map to insert into.
* @param container Either a map, or a list of pairs to insert into the map.
* @param maxItems Maximum number of items to insert into the map from the container.
*/
public abstract EnsoHashMap executeInsertAll(
VirtualFrame frame, EnsoHashMap self, EnsoHashMap container, int maxItems);
@Specialization
EnsoHashMap doEnsoHashMaps(
VirtualFrame frame,
EnsoHashMap self,
EnsoHashMap other,
int maxItems,
@Cached HashCodeNode hashCodeNode,
@Cached EqualsNode equalsNode) {
assert maxItems > 0;
var selfSize = self.getHashSize();
var otherSize = other.getHashSize();
if (otherSize == 0) {
return self;
}
var mapBuilder = EnsoHashMapBuilder.createWithCapacity(selfSize + otherSize);
var selfMapBuilder = self.getMapBuilder(frame, true, hashCodeNode, equalsNode);
var selfEntriesIt = selfMapBuilder.getEntriesIterator(selfMapBuilder.generation());
while (selfEntriesIt.hasNext()) {
var selfEntry = selfEntriesIt.next();
mapBuilder.put(frame, selfEntry.key(), selfEntry.value(), hashCodeNode, equalsNode);
}
var otherMapBuilder = other.getMapBuilder(frame, true, hashCodeNode, equalsNode);
var otherEntriesIt = otherMapBuilder.getEntriesIterator(otherMapBuilder.generation());
var itemsInserted = 0;
while (otherEntriesIt.hasNext()) {
if (itemsInserted >= maxItems) {
break;
}
var entry = otherEntriesIt.next();
mapBuilder.put(frame, entry.key(), entry.value(), hashCodeNode, equalsNode);
itemsInserted++;
}
return mapBuilder.build();
}
}

View File

@ -3,6 +3,7 @@ package org.enso.interpreter.runtime.data.hash;
import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Cached.Shared; import com.oracle.truffle.api.dsl.Cached.Shared;
import com.oracle.truffle.api.dsl.GenerateUncached;
import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.InteropLibrary;
@ -25,6 +26,7 @@ import org.enso.interpreter.runtime.error.PanicException;
Returns newly created hash map with the given key value mapping. Returns newly created hash map with the given key value mapping.
""", """,
autoRegister = false) autoRegister = false)
@GenerateUncached
public abstract class HashMapInsertNode extends Node { public abstract class HashMapInsertNode extends Node {
public static HashMapInsertNode build() { public static HashMapInsertNode build() {
@ -41,6 +43,7 @@ public abstract class HashMapInsertNode extends Node {
Object value, Object value,
@Shared("hash") @Cached HashCodeNode hashCodeNode, @Shared("hash") @Cached HashCodeNode hashCodeNode,
@Shared("equals") @Cached EqualsNode equalsNode) { @Shared("equals") @Cached EqualsNode equalsNode) {
assert value != null;
var mapBuilder = hashMap.getMapBuilder(frame, false, hashCodeNode, equalsNode); var mapBuilder = hashMap.getMapBuilder(frame, false, hashCodeNode, equalsNode);
mapBuilder.put(frame, key, value, hashCodeNode, equalsNode); mapBuilder.put(frame, key, value, hashCodeNode, equalsNode);
var newMap = mapBuilder.build(); var newMap = mapBuilder.build();
@ -61,6 +64,7 @@ public abstract class HashMapInsertNode extends Node {
@CachedLibrary(limit = "3") InteropLibrary iteratorInterop, @CachedLibrary(limit = "3") InteropLibrary iteratorInterop,
@Shared("hash") @Cached HashCodeNode hashCodeNode, @Shared("hash") @Cached HashCodeNode hashCodeNode,
@Shared("equals") @Cached EqualsNode equalsNode) { @Shared("equals") @Cached EqualsNode equalsNode) {
assert valueToInsert != null;
var mapBuilder = EnsoHashMapBuilder.create(); var mapBuilder = EnsoHashMapBuilder.create();
try { try {
Object entriesIterator = mapInterop.getHashEntriesIterator(foreignMap); Object entriesIterator = mapInterop.getHashEntriesIterator(foreignMap);

View File

@ -23,6 +23,10 @@ public abstract class HashMapSizeNode extends Node {
return HashMapSizeNodeGen.create(); return HashMapSizeNodeGen.create();
} }
public static HashMapSizeNode getUncached() {
return HashMapSizeNodeGen.getUncached();
}
public abstract long execute(Object self); public abstract long execute(Object self);
@Specialization(guards = "interop.hasHashEntries(hashMap)", limit = "3") @Specialization(guards = "interop.hasHashEntries(hashMap)", limit = "3")

View File

@ -13,15 +13,19 @@ import com.oracle.truffle.api.library.ExportMessage;
import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.profiles.BranchProfile; import com.oracle.truffle.api.profiles.BranchProfile;
import java.util.Arrays; import java.util.Arrays;
import java.util.Comparator;
import org.enso.interpreter.dsl.Builtin; import org.enso.interpreter.dsl.Builtin;
import org.enso.interpreter.runtime.EnsoContext; import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.data.EnsoObject; import org.enso.interpreter.runtime.data.EnsoObject;
import org.enso.interpreter.runtime.data.Type; import org.enso.interpreter.runtime.data.Type;
import org.enso.interpreter.runtime.error.Warning; import org.enso.interpreter.runtime.data.hash.EnsoHashMap;
import org.enso.interpreter.runtime.error.WarningsLibrary; import org.enso.interpreter.runtime.data.hash.HashMapInsertAllNode;
import org.enso.interpreter.runtime.error.WithWarnings; import org.enso.interpreter.runtime.data.hash.HashMapInsertNode;
import org.enso.interpreter.runtime.data.hash.HashMapSizeNode;
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary; import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
import org.graalvm.collections.EconomicSet; import org.enso.interpreter.runtime.warning.AppendWarningNode;
import org.enso.interpreter.runtime.warning.Warning;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
/** A primitive boxed array type for use in the runtime. */ /** A primitive boxed array type for use in the runtime. */
@ExportLibrary(InteropLibrary.class) @ExportLibrary(InteropLibrary.class)
@ -30,9 +34,13 @@ import org.graalvm.collections.EconomicSet;
@Builtin(pkg = "mutable", stdlibName = "Standard.Base.Data.Array.Array") @Builtin(pkg = "mutable", stdlibName = "Standard.Base.Data.Array.Array")
final class Array implements EnsoObject { final class Array implements EnsoObject {
private final Object[] items; private final Object[] items;
/** If true, some elements contain warning, and thus, this Array contains warning. */
private Boolean withWarnings; private Boolean withWarnings;
private Warning[] cachedWarningsWrapped;
private Warning[] cachedWarningsUnwrapped; private EnsoHashMap cachedWarningsWrapped;
private EnsoHashMap cachedWarningsUnwrapped;
/** /**
* Creates a new array * Creates a new array
@ -91,7 +99,12 @@ final class Array implements EnsoObject {
long index, long index,
@CachedLibrary(limit = "3") WarningsLibrary warnings, @CachedLibrary(limit = "3") WarningsLibrary warnings,
@Cached BranchProfile errProfile, @Cached BranchProfile errProfile,
@Cached BranchProfile hasWarningsProfile) @Cached BranchProfile hasWarningsProfile,
@Cached HashMapInsertNode mapInsertNode,
@Cached AppendWarningNode appendWarningNode,
@Cached BranchProfile shouldWrapProfile,
@Cached HashMapSizeNode mapSizeNode,
@Cached HashMapInsertAllNode mapInsertAllNode)
throws InvalidArrayIndexException, UnsupportedMessageException { throws InvalidArrayIndexException, UnsupportedMessageException {
if (index >= items.length || index < 0) { if (index >= items.length || index < 0) {
errProfile.enter(); errProfile.enter();
@ -101,11 +114,13 @@ final class Array implements EnsoObject {
var v = items[(int) index]; var v = items[(int) index];
if (this.hasWarnings(warnings)) { if (this.hasWarnings(warnings)) {
hasWarningsProfile.enter(); hasWarningsProfile.enter();
Warning[] extracted = this.getWarnings(null, false, warnings); var extractedWarnsMap =
this.getWarnings(
false, warnings, mapInsertNode, shouldWrapProfile, mapSizeNode, mapInsertAllNode);
if (warnings.hasWarnings(v)) { if (warnings.hasWarnings(v)) {
v = warnings.removeWarnings(v); v = warnings.removeWarnings(v);
} }
return WithWarnings.wrap(EnsoContext.get(warnings), v, extracted); return appendWarningNode.executeAppend(null, v, extractedWarnsMap);
} }
return v; return v;
@ -180,48 +195,81 @@ final class Array implements EnsoObject {
} }
@ExportMessage @ExportMessage
Warning[] getWarnings( EnsoHashMap getWarnings(
Node location,
boolean shouldWrap, boolean shouldWrap,
@Shared("warnsLib") @CachedLibrary(limit = "3") WarningsLibrary warnings) @Shared("warnsLib") @CachedLibrary(limit = "3") WarningsLibrary warnings,
@Shared("mapInsertNode") @Cached HashMapInsertNode mapInsertNode,
@Shared @Cached BranchProfile shouldWrapProfile,
@Shared @Cached HashMapSizeNode mapSizeNode,
@Shared @Cached HashMapInsertAllNode mapInsertAllNode)
throws UnsupportedMessageException { throws UnsupportedMessageException {
Warning[] cache = shouldWrap ? cachedWarningsWrapped : cachedWarningsUnwrapped; var cache = shouldWrap ? cachedWarningsWrapped : cachedWarningsUnwrapped;
if (cache == null) { if (cache == null) {
cache = Warning.fromSetToArray(collectAllWarnings(warnings, location, shouldWrap)); var warnLimit = EnsoContext.get(warnings).getWarningsLimit();
var allWarnsMap =
collectAllWarnings(
warnings,
mapInsertNode,
shouldWrap,
mapInsertAllNode,
warnLimit,
shouldWrapProfile,
mapSizeNode);
if (shouldWrap) { if (shouldWrap) {
cachedWarningsWrapped = cache; cachedWarningsWrapped = allWarnsMap;
cache = cachedWarningsWrapped;
} else { } else {
cachedWarningsUnwrapped = cache; cachedWarningsUnwrapped = allWarnsMap;
cache = cachedWarningsUnwrapped;
} }
} }
assert cache != null;
return cache; return cache;
} }
@CompilerDirectives.TruffleBoundary private EnsoHashMap collectAllWarnings(
private EconomicSet<Warning> collectAllWarnings( WarningsLibrary warningsLib,
WarningsLibrary warningsLib, Node location, boolean shouldWrap) HashMapInsertNode mapInsertNode,
boolean shouldWrap,
HashMapInsertAllNode mapInsertAllNode,
int warnLimit,
BranchProfile shouldWrapProfile,
HashMapSizeNode mapSizeNode)
throws UnsupportedMessageException { throws UnsupportedMessageException {
EconomicSet<Warning> setOfWarnings = EconomicSet.create(new WithWarnings.WarningEquivalence()); var warnsSet = EnsoHashMap.empty();
for (int i = 0; i < this.items.length; i++) { for (int itemIdx = 0; itemIdx < this.items.length; itemIdx++) {
final int finalIndex = i; Object item = this.items[itemIdx];
Object item = this.items[i]; var warnsCnt = (int) mapSizeNode.execute(warnsSet);
if (warnsCnt == warnLimit) {
break;
}
if (warningsLib.hasWarnings(item)) { if (warningsLib.hasWarnings(item)) {
Warning[] warnings = warningsLib.getWarnings(item, location, shouldWrap); var itemWarnsMap = warningsLib.getWarnings(item, shouldWrap);
Warning[] wrappedWarningsMaybe; assert mapSizeNode.execute(itemWarnsMap) <= warnLimit;
if (shouldWrap) { if (!shouldWrap) {
wrappedWarningsMaybe = warnsSet =
Arrays.stream(warnings) mapInsertAllNode.executeInsertAll(null, warnsSet, itemWarnsMap, warnLimit - warnsCnt);
.map(warning -> Warning.wrapMapError(warningsLib, warning, finalIndex))
.toArray(Warning[]::new);
} else { } else {
wrappedWarningsMaybe = warnings; shouldWrapProfile.enter();
CompilerDirectives.transferToInterpreter();
// warnings need to be sorted such that at the first index, there is the oldest warning.
// This is because we are creating new warnings by wrapping the previous one, and we need
// to
// do that in the same creation order.
var warnings = Warning.fromMapToArray(itemWarnsMap);
Arrays.sort(warnings, Comparator.comparing(Warning::getSequenceId));
for (int i = 0; i < Math.min(warnings.length, warnLimit); i++) {
var warn = warnings[i];
var wrappedWarn = Warning.wrapMapError(warningsLib, warn, itemIdx);
warnsSet =
mapInsertNode.execute(null, warnsSet, wrappedWarn.getSequenceId(), wrappedWarn);
}
} }
setOfWarnings.addAll(Arrays.asList(wrappedWarningsMaybe));
} }
} }
return setOfWarnings; assert mapSizeNode.execute(warnsSet) <= warnLimit;
return warnsSet;
} }
@ExportMessage @ExportMessage
@ -239,10 +287,18 @@ final class Array implements EnsoObject {
} }
@ExportMessage @ExportMessage
boolean isLimitReached(@Shared("warnsLib") @CachedLibrary(limit = "3") WarningsLibrary warnings) { boolean isLimitReached(
@Shared("warnsLib") @CachedLibrary(limit = "3") WarningsLibrary warnsLib,
@Shared("mapInsertNode") @Cached HashMapInsertNode mapInsertNode,
@Shared @Cached HashMapInsertAllNode mapInsertAllNode,
@Shared @Cached HashMapSizeNode mapSizeNode,
@Shared @Cached BranchProfile shouldWrapProfile) {
try { try {
int limit = EnsoContext.get(warnings).getWarningsLimit(); int limit = EnsoContext.get(warnsLib).getWarningsLimit();
return getWarnings(null, false, warnings).length >= limit; var ourWarnings =
getWarnings(
false, warnsLib, mapInsertNode, shouldWrapProfile, mapSizeNode, mapInsertAllNode);
return (int) mapSizeNode.execute(ourWarnings) >= limit;
} catch (UnsupportedMessageException e) { } catch (UnsupportedMessageException e) {
return false; return false;
} }

View File

@ -12,7 +12,7 @@ import com.oracle.truffle.api.library.ExportMessage;
import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.Node;
import java.util.Arrays; import java.util.Arrays;
import org.enso.interpreter.runtime.data.EnsoObject; import org.enso.interpreter.runtime.data.EnsoObject;
import org.enso.interpreter.runtime.error.WarningsLibrary; import org.enso.interpreter.runtime.warning.WarningsLibrary;
@ExportLibrary(InteropLibrary.class) @ExportLibrary(InteropLibrary.class)
final class ArrayBuilder implements EnsoObject { final class ArrayBuilder implements EnsoObject {
@ -142,14 +142,31 @@ final class ArrayBuilder implements EnsoObject {
} }
} }
private static boolean checkArraySize(boolean mustBeExact, int real, int expected) {
if (real == expected) {
return true;
} else {
if (mustBeExact) {
CompilerDirectives.transferToInterpreter();
}
return false;
}
}
/** Returns the current array of the builder. */ /** Returns the current array of the builder. */
private Object toArray() { private Object toArray(boolean mustBeExact) {
if (objectArray != null) { if (objectArray != null) {
return objectArray.length == size ? objectArray : Arrays.copyOf(objectArray, size); return checkArraySize(mustBeExact, objectArray.length, size)
? objectArray
: Arrays.copyOf(objectArray, size);
} else if (primitiveArray instanceof long[] longArray) { } else if (primitiveArray instanceof long[] longArray) {
return longArray.length == size ? longArray : Arrays.copyOf(longArray, size); return checkArraySize(mustBeExact, longArray.length, size)
? longArray
: Arrays.copyOf(longArray, size);
} else if (primitiveArray instanceof double[] doubleArray) { } else if (primitiveArray instanceof double[] doubleArray) {
return doubleArray.length == size ? doubleArray : Arrays.copyOf(doubleArray, size); return checkArraySize(mustBeExact, doubleArray.length, size)
? doubleArray
: Arrays.copyOf(doubleArray, size);
} else { } else {
return null; return null;
} }
@ -195,7 +212,7 @@ final class ArrayBuilder implements EnsoObject {
yield get(index, iop); yield get(index, iop);
} }
case "getSize" -> getSize(); case "getSize" -> getSize();
case "toArray" -> asVector(); case "toArray" -> asVector(false);
default -> throw UnknownIdentifierException.create(name); default -> throw UnknownIdentifierException.create(name);
}; };
} }
@ -225,8 +242,8 @@ final class ArrayBuilder implements EnsoObject {
return "Array_Builder"; return "Array_Builder";
} }
Object asVector() { Object asVector(boolean mustBeExact) {
var res = toArray(); var res = toArray(mustBeExact);
if (res instanceof long[] longs) { if (res instanceof long[] longs) {
return Vector.fromLongArray(longs); return Vector.fromLongArray(longs);
} }

View File

@ -1,6 +1,7 @@
package org.enso.interpreter.runtime.data.vector; package org.enso.interpreter.runtime.data.vector;
import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.GenerateUncached;
import com.oracle.truffle.api.dsl.NeverDefault; import com.oracle.truffle.api.dsl.NeverDefault;
import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.exception.AbstractTruffleException; import com.oracle.truffle.api.exception.AbstractTruffleException;
@ -10,8 +11,10 @@ import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.library.CachedLibrary; import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.node.expression.builtin.interop.syntax.HostValueToEnsoNode; import org.enso.interpreter.node.expression.builtin.interop.syntax.HostValueToEnsoNode;
import org.enso.interpreter.runtime.error.WarningsLibrary; import org.enso.interpreter.runtime.warning.AppendWarningNode;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
@GenerateUncached
public abstract class ArrayLikeAtNode extends Node { public abstract class ArrayLikeAtNode extends Node {
public abstract Object executeAt(Object arrayLike, long index) throws InvalidArrayIndexException; public abstract Object executeAt(Object arrayLike, long index) throws InvalidArrayIndexException;
@ -57,10 +60,11 @@ public abstract class ArrayLikeAtNode extends Node {
long index, long index,
@Cached.Exclusive @CachedLibrary(limit = "3") InteropLibrary interop, @Cached.Exclusive @CachedLibrary(limit = "3") InteropLibrary interop,
@Cached.Exclusive @CachedLibrary(limit = "3") WarningsLibrary warnings, @Cached.Exclusive @CachedLibrary(limit = "3") WarningsLibrary warnings,
@Cached.Exclusive @Cached HostValueToEnsoNode convert) @Cached.Exclusive @Cached HostValueToEnsoNode convert,
@Cached AppendWarningNode appendWarningNode)
throws InvalidArrayIndexException { throws InvalidArrayIndexException {
try { try {
return self.readArrayElement(index, interop, warnings, convert); return self.readArrayElement(index, interop, warnings, convert, appendWarningNode);
} catch (UnsupportedMessageException ex) { } catch (UnsupportedMessageException ex) {
throw ArrayPanics.notAnArrayPanic(this, self); throw ArrayPanics.notAnArrayPanic(this, self);
} }

View File

@ -11,8 +11,8 @@ import org.enso.interpreter.node.callable.dispatch.InvokeFunctionNode;
import org.enso.interpreter.runtime.callable.function.Function; import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.data.EnsoObject; import org.enso.interpreter.runtime.data.EnsoObject;
import org.enso.interpreter.runtime.error.DataflowError; import org.enso.interpreter.runtime.error.DataflowError;
import org.enso.interpreter.runtime.error.WarningsLibrary;
import org.enso.interpreter.runtime.state.State; import org.enso.interpreter.runtime.state.State;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
/** Publicly available operations on array-like classes. */ /** Publicly available operations on array-like classes. */
@Builtin(pkg = "immutable", stdlibName = "Standard.Base.Internal.Array_Like_Helpers") @Builtin(pkg = "immutable", stdlibName = "Standard.Base.Internal.Array_Like_Helpers")
@ -97,7 +97,7 @@ public final class ArrayLikeHelpers {
} }
target.add(value, warnings); target.add(value, warnings);
} }
return target.asVector(); return target.asVector(true);
} }
@Builtin.Method( @Builtin.Method(

View File

@ -2,6 +2,7 @@ package org.enso.interpreter.runtime.data.vector;
import com.oracle.truffle.api.dsl.Cached.Exclusive; import com.oracle.truffle.api.dsl.Cached.Exclusive;
import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.GenerateUncached;
import com.oracle.truffle.api.dsl.NeverDefault; import com.oracle.truffle.api.dsl.NeverDefault;
import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.InteropLibrary;
@ -9,6 +10,7 @@ import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.library.CachedLibrary; import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.Node;
@GenerateUncached
public abstract class ArrayLikeLengthNode extends Node { public abstract class ArrayLikeLengthNode extends Node {
public abstract long executeLength(Object arrayLike); public abstract long executeLength(Object arrayLike);

View File

@ -15,10 +15,10 @@ import org.enso.interpreter.node.expression.builtin.interop.syntax.HostValueToEn
import org.enso.interpreter.runtime.EnsoContext; import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.data.EnsoObject; import org.enso.interpreter.runtime.data.EnsoObject;
import org.enso.interpreter.runtime.data.Type; import org.enso.interpreter.runtime.data.Type;
import org.enso.interpreter.runtime.error.Warning; import org.enso.interpreter.runtime.data.hash.EnsoHashMap;
import org.enso.interpreter.runtime.error.WarningsLibrary;
import org.enso.interpreter.runtime.error.WithWarnings;
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary; import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
import org.enso.interpreter.runtime.warning.AppendWarningNode;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
@ExportLibrary(TypesLibrary.class) @ExportLibrary(TypesLibrary.class)
@ExportLibrary(InteropLibrary.class) @ExportLibrary(InteropLibrary.class)
@ -91,7 +91,8 @@ final class ArraySlice implements EnsoObject {
long index, long index,
@Shared("interop") @CachedLibrary(limit = "3") InteropLibrary interop, @Shared("interop") @CachedLibrary(limit = "3") InteropLibrary interop,
@CachedLibrary(limit = "3") WarningsLibrary warnings, @CachedLibrary(limit = "3") WarningsLibrary warnings,
@Cached HostValueToEnsoNode toEnso) @Cached HostValueToEnsoNode toEnso,
@Cached AppendWarningNode appendWarningNode)
throws InvalidArrayIndexException, UnsupportedMessageException { throws InvalidArrayIndexException, UnsupportedMessageException {
if (index < 0 || index >= getArraySize(interop)) { if (index < 0 || index >= getArraySize(interop)) {
throw InvalidArrayIndexException.create(index); throw InvalidArrayIndexException.create(index);
@ -99,11 +100,11 @@ final class ArraySlice implements EnsoObject {
var v = interop.readArrayElement(storage, start + index); var v = interop.readArrayElement(storage, start + index);
if (this.hasWarnings(warnings)) { if (this.hasWarnings(warnings)) {
Warning[] extracted = this.getWarnings(null, false, warnings); var extracted = this.getWarnings(false, warnings);
if (warnings.hasWarnings(v)) { if (warnings.hasWarnings(v)) {
v = warnings.removeWarnings(v); v = warnings.removeWarnings(v);
} }
return WithWarnings.wrap(EnsoContext.get(warnings), toEnso.execute(v), extracted); return appendWarningNode.executeAppend(null, toEnso.execute(v), extracted);
} }
return toEnso.execute(v); return toEnso.execute(v);
} }
@ -155,12 +156,10 @@ final class ArraySlice implements EnsoObject {
} }
@ExportMessage @ExportMessage
Warning[] getWarnings( EnsoHashMap getWarnings(
Node location, boolean shouldWrap, @Shared("warnsLib") @CachedLibrary(limit = "3") WarningsLibrary warnings)
boolean shouldWrap,
@Shared("warnsLib") @CachedLibrary(limit = "3") WarningsLibrary warnings)
throws UnsupportedMessageException { throws UnsupportedMessageException {
return warnings.getWarnings(this.storage, location, shouldWrap); return warnings.getWarnings(this.storage, shouldWrap);
} }
@ExportMessage @ExportMessage

View File

@ -15,10 +15,10 @@ import org.enso.interpreter.node.expression.builtin.interop.syntax.HostValueToEn
import org.enso.interpreter.runtime.EnsoContext; import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.data.EnsoObject; import org.enso.interpreter.runtime.data.EnsoObject;
import org.enso.interpreter.runtime.data.Type; import org.enso.interpreter.runtime.data.Type;
import org.enso.interpreter.runtime.error.Warning; import org.enso.interpreter.runtime.data.hash.EnsoHashMap;
import org.enso.interpreter.runtime.error.WarningsLibrary;
import org.enso.interpreter.runtime.error.WithWarnings;
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary; import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
import org.enso.interpreter.runtime.warning.AppendWarningNode;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
@ExportLibrary(InteropLibrary.class) @ExportLibrary(InteropLibrary.class)
@ExportLibrary(TypesLibrary.class) @ExportLibrary(TypesLibrary.class)
@ -178,8 +178,8 @@ abstract class Vector implements EnsoObject {
} }
@ExportMessage @ExportMessage
Warning[] getWarnings(Node location, boolean shouldWrap) throws UnsupportedMessageException { EnsoHashMap getWarnings(boolean shouldWrap) {
return new Warning[0]; return EnsoHashMap.empty();
} }
@ExportMessage @ExportMessage
@ -249,15 +249,16 @@ abstract class Vector implements EnsoObject {
long index, long index,
@Cached.Shared(value = "interop") @CachedLibrary(limit = "3") InteropLibrary interop, @Cached.Shared(value = "interop") @CachedLibrary(limit = "3") InteropLibrary interop,
@CachedLibrary(limit = "3") WarningsLibrary warnings, @CachedLibrary(limit = "3") WarningsLibrary warnings,
@Cached HostValueToEnsoNode toEnso) @Cached HostValueToEnsoNode toEnso,
@Cached AppendWarningNode appendWarningNode)
throws InvalidArrayIndexException, UnsupportedMessageException { throws InvalidArrayIndexException, UnsupportedMessageException {
var v = interop.readArrayElement(this.storage, index); var v = interop.readArrayElement(this.storage, index);
if (warnings.hasWarnings(this.storage)) { if (warnings.hasWarnings(this.storage)) {
Warning[] extracted = warnings.getWarnings(this.storage, null, false); var extracted = warnings.getWarnings(this.storage, false);
if (warnings.hasWarnings(v)) { if (warnings.hasWarnings(v)) {
v = warnings.removeWarnings(v); v = warnings.removeWarnings(v);
} }
return WithWarnings.wrap(EnsoContext.get(interop), toEnso.execute(v), extracted); return appendWarningNode.executeAppend(null, toEnso.execute(v), extracted);
} }
return toEnso.execute(v); return toEnso.execute(v);
} }
@ -287,12 +288,11 @@ abstract class Vector implements EnsoObject {
} }
@ExportMessage @ExportMessage
Warning[] getWarnings( EnsoHashMap getWarnings(
Node location,
boolean shouldWrap, boolean shouldWrap,
@Cached.Shared(value = "warnsLib") @CachedLibrary(limit = "3") WarningsLibrary warnings) @Cached.Shared(value = "warnsLib") @CachedLibrary(limit = "3") WarningsLibrary warnings)
throws UnsupportedMessageException { throws UnsupportedMessageException {
return warnings.getWarnings(this.storage, location, shouldWrap); return warnings.getWarnings(this.storage, shouldWrap);
} }
@ExportMessage @ExportMessage
@ -357,8 +357,8 @@ abstract class Vector implements EnsoObject {
} }
@ExportMessage @ExportMessage
Warning[] getWarnings(Node location, boolean shouldWrap) throws UnsupportedMessageException { EnsoHashMap getWarnings(boolean shouldWrap) {
return new Warning[0]; return EnsoHashMap.empty();
} }
@ExportMessage @ExportMessage
@ -407,8 +407,8 @@ abstract class Vector implements EnsoObject {
} }
@ExportMessage @ExportMessage
Warning[] getWarnings(Node location, boolean shouldWrap) throws UnsupportedMessageException { EnsoHashMap getWarnings(boolean shouldWrap) {
return new Warning[0]; return EnsoHashMap.empty();
} }
@ExportMessage @ExportMessage

View File

@ -1,258 +0,0 @@
package org.enso.interpreter.runtime.error;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Bind;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.InvalidArrayIndexException;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.RootNode;
import com.oracle.truffle.api.source.SourceSection;
import java.util.Arrays;
import java.util.Comparator;
import org.enso.interpreter.dsl.Builtin;
import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.data.ArrayRope;
import org.enso.interpreter.runtime.data.EnsoObject;
import org.enso.interpreter.runtime.data.Type;
import org.enso.interpreter.runtime.data.vector.ArrayLikeHelpers;
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
import org.graalvm.collections.EconomicSet;
@Builtin(pkg = "error", stdlibName = "Standard.Base.Warning.Warning")
@ExportLibrary(TypesLibrary.class)
public final class Warning implements EnsoObject {
private final Object value;
private final Object origin;
private final ArrayRope<Reassignment> reassignments;
private final long sequenceId;
private Warning(Object value, Object origin, long sequenceId) {
this(value, origin, sequenceId, new ArrayRope<>());
}
private Warning(
Object value, Object origin, long sequenceId, ArrayRope<Reassignment> reassignments) {
this.value = value;
this.origin = origin;
this.reassignments = reassignments;
this.sequenceId = sequenceId;
}
@Builtin.Method(name = "value", description = "Gets the payload of the warning.")
@SuppressWarnings("generic-enso-builtin-type")
public Object getValue() {
return value;
}
@Builtin.Method(name = "origin", description = "Gets the payload of the warning.")
@SuppressWarnings("generic-enso-builtin-type")
public Object getOrigin() {
return origin;
}
@Builtin.Method(
name = "create",
description = "Creates a new instance of the primitive warning value.",
autoRegister = false)
@Builtin.Specialize
public static Warning create(EnsoContext ctx, Object payload, Object origin) {
return new Warning(payload, origin, ctx.nextSequenceId());
}
@Builtin.Method(description = "Gets the list of locations where the warnings was reassigned.")
public EnsoObject getReassignments() {
Warning.Reassignment[] reassignmentsArray = reassignments.toArray(Warning.Reassignment[]::new);
return ArrayLikeHelpers.wrapEnsoObjects(reassignmentsArray);
}
@Builtin.Method(
name = "attach_with_stacktrace",
description = "Attaches the given warning to the value.",
autoRegister = false)
@Builtin.Specialize
public static WithWarnings attach(
EnsoContext ctx, WithWarnings value, Object warning, Object origin) {
return value.append(ctx, new Warning(warning, origin, ctx.nextSequenceId()));
}
@Builtin.Method(
name = "attach_with_stacktrace",
description = "Attaches the given warning to the value.",
autoRegister = false)
@Builtin.Specialize(fallback = true)
public static WithWarnings attach(EnsoContext ctx, Object value, Object warning, Object origin) {
return WithWarnings.wrap(ctx, value, new Warning(warning, origin, ctx.nextSequenceId()));
}
@Builtin.Method(
name = "get_all_vector",
description = "Gets all the warnings associated with the value.",
autoRegister = false)
@Builtin.Specialize
@CompilerDirectives.TruffleBoundary
public static EnsoObject getAll(
WithWarnings value, boolean shouldWrap, WarningsLibrary warningsLib) {
Warning[] warnings = value.getWarningsArray(warningsLib, shouldWrap);
sortArray(warnings);
return ArrayLikeHelpers.asVectorEnsoObjects(warnings);
}
@Builtin.Method(
name = "get_all_vector",
description = "Gets all the warnings associated with the value.",
autoRegister = false)
@Builtin.Specialize(fallback = true)
public static EnsoObject getAll(Object value, boolean shouldWrap, WarningsLibrary warningsLib) {
if (warningsLib.hasWarnings(value)) {
try {
Warning[] warnings = warningsLib.getWarnings(value, null, shouldWrap);
sortArray(warnings);
return ArrayLikeHelpers.asVectorEnsoObjects(warnings);
} catch (UnsupportedMessageException e) {
throw EnsoContext.get(warningsLib).raiseAssertionPanic(warningsLib, null, e);
}
} else {
return ArrayLikeHelpers.asVectorEmpty();
}
}
@Builtin.Method(
description =
"Returns `true` if the maximal number of warnings has been reached, `false` otherwise.",
autoRegister = false)
@Builtin.Specialize
public static boolean limitReached(WithWarnings value, WarningsLibrary warnings) {
return value.isLimitReached();
}
@Builtin.Method(
description =
"Returns `true` if the maximal number of warnings has been reached, `false` otherwise.",
autoRegister = false)
@Builtin.Specialize(fallback = true)
public static boolean limitReached(Object value, WarningsLibrary warnings) {
return warnings.hasWarnings(value) ? warnings.isLimitReached(value) : false;
}
@CompilerDirectives.TruffleBoundary
private static void sortArray(Warning[] arr) {
Arrays.sort(arr, Comparator.comparing(Warning::getSequenceId).reversed());
}
/** Converts set to an array behing a truffle boundary. */
@CompilerDirectives.TruffleBoundary
public static Warning[] fromSetToArray(EconomicSet<Warning> set) {
return set.toArray(new Warning[set.size()]);
}
@Builtin.Method(
name = "set_array",
description = "Sets all the warnings associated with the value.",
autoRegister = false)
@Builtin.Specialize
@SuppressWarnings("generic-enso-builtin-type")
public static Object set(
EnsoContext ctx, WithWarnings value, Object warnings, InteropLibrary interop) {
return setGeneric(ctx, value.getValue(), interop, warnings);
}
@Builtin.Method(
name = "set_array",
description = "Sets all the warnings associated with the value.",
autoRegister = false)
@SuppressWarnings("generic-enso-builtin-type")
@Builtin.Specialize(fallback = true)
public static Object set(EnsoContext ctx, Object value, Object warnings, InteropLibrary interop) {
return setGeneric(ctx, value, interop, warnings);
}
private static Object setGeneric(
EnsoContext ctx, Object value, InteropLibrary interop, Object warnings) {
try {
var size = interop.getArraySize(warnings);
if (size == 0) {
return value;
}
Warning[] warningsCast = new Warning[(int) size];
for (int i = 0; i < warningsCast.length; i++) {
warningsCast[i] = (Warning) interop.readArrayElement(warnings, i);
}
return WithWarnings.wrap(ctx, value, warningsCast);
} catch (UnsupportedMessageException | InvalidArrayIndexException ex) {
throw EnsoContext.get(interop).raiseAssertionPanic(interop, null, ex);
}
}
@CompilerDirectives.TruffleBoundary
@Override
public String toString() {
return value.toString();
}
@ExportLibrary(InteropLibrary.class)
public static final class Reassignment implements EnsoObject {
private final String methodName;
private final SourceSection location;
public Reassignment(String methodName, SourceSection location) {
this.methodName = methodName;
this.location = location;
}
@ExportMessage
boolean hasExecutableName() {
return true;
}
@ExportMessage
String getExecutableName() {
return methodName;
}
@ExportMessage
boolean hasSourceLocation() {
return location != null;
}
@ExportMessage
SourceSection getSourceLocation() throws UnsupportedMessageException {
if (location == null) {
throw UnsupportedMessageException.create();
}
return location;
}
}
public long getSequenceId() {
return sequenceId;
}
@CompilerDirectives.TruffleBoundary
public Warning reassign(Node location) {
RootNode root = location.getRootNode();
SourceSection section = location.getEncapsulatingSourceSection();
Reassignment reassignment = new Reassignment(root == null ? "" : root.getName(), section);
return new Warning(value, origin, sequenceId, reassignments.prepend(reassignment));
}
@ExportMessage
boolean hasType() {
return true;
}
@ExportMessage
Type getType(@Bind("$node") Node node) {
return EnsoContext.get(node).getBuiltins().warning();
}
public static Warning wrapMapError(WarningsLibrary warningsLib, Warning warning, long index) {
var ctx = EnsoContext.get(warningsLib);
var error = warning.getValue();
var wrappedError = ctx.getBuiltins().error().makeMapError(index, error);
var wrappedWarning = Warning.create(ctx, wrappedError, warning.getOrigin());
return wrappedWarning;
}
}

View File

@ -1,374 +0,0 @@
package org.enso.interpreter.runtime.error;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Bind;
import com.oracle.truffle.api.dsl.Cached.Shared;
import com.oracle.truffle.api.interop.ArityException;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage;
import com.oracle.truffle.api.library.Message;
import com.oracle.truffle.api.library.ReflectionLibrary;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.node.callable.InteropMethodCallNode;
import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
import org.enso.interpreter.runtime.data.ArrayRope;
import org.enso.interpreter.runtime.data.EnsoObject;
import org.enso.interpreter.runtime.data.Type;
import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
import org.enso.interpreter.runtime.state.State;
import org.graalvm.collections.EconomicSet;
import org.graalvm.collections.Equivalence;
/**
* Represents a typical Enso <em>value with warnings</em>. As much of care as possible is taken to
* delegate all operations to the underlaying {@code value}. Warnings are considered {@link
* InteropLibrary#isException exceptional values} - e.g. one can check for them in Java polyglot
* code as:
*
* <pre>
* Value value = ...;
* if (value.fitsInLong() && value.isException()) {
* // probably an Integer with a warning
* try {
* warningMulti.throwException();
* } catch (PolyglotException ex) {
* System.out.println("Warnings attached to " + value.asLong() + " are " + ex.getMessage());
* }
* }
* </pre>
*/
@ExportLibrary(TypesLibrary.class)
@ExportLibrary(WarningsLibrary.class)
@ExportLibrary(ReflectionLibrary.class)
@ExportLibrary(value = InteropLibrary.class, delegateTo = "value")
public final class WithWarnings implements EnsoObject {
final Object value;
private final EconomicSet<Warning> warnings;
private final boolean limitReached;
private final int maxWarnings;
/**
* Creates a new instance of value wrapped in warnings. `limitReached` parameter allows for
* indicating if some custom warnings filtering on `warnings` have already been performed.
*
* @param value value to be wrapped in warnings
* @param maxWarnings maximal number of warnings allowed to be attached to the value
* @param limitReached if `true`, indicates that `warnings` have already been limited for a
* custom-method, `false` otherwise
* @param warnings non-empty warnings to be attached to a value
*/
private WithWarnings(Object value, int maxWarnings, boolean limitReached, Warning... warnings) {
assert isAcceptableValue(value);
this.warnings = createSetFromArray(maxWarnings, warnings);
assert this.warnings.size() > 0;
this.value = value;
this.limitReached = limitReached || this.warnings.size() >= maxWarnings;
this.maxWarnings = maxWarnings;
}
private WithWarnings(Object value, int maxWarnings, Warning... warnings) {
this(value, maxWarnings, false, warnings);
}
/**
* Creates a new instance of value wrapped in warnings. `limitReached` parameter allows for
* indicating if some custom warnings filtering on `additionalWarnings` have already been
* performed.
*
* @param value value to be wrapped in warnings
* @param maxWarnings maximal number of warnings allowed to be attached to the value
* @param warnings warnings originally attached to a value
* @param limitReached if `true`, indicates that `warnings` have already been limited for a
* custom-method, `false` otherwise
* @param additionalWarnings additional warnings to be appended to the list of `warnings`
*/
private WithWarnings(
Object value,
int maxWarnings,
EconomicSet<Warning> warnings,
boolean limitReached,
Warning... additionalWarnings) {
assert isAcceptableValue(value);
this.warnings = cloneSetAndAppend(maxWarnings, warnings, additionalWarnings);
assert this.warnings.size() > 0;
this.value = value;
this.limitReached = limitReached || this.warnings.size() >= maxWarnings;
this.maxWarnings = maxWarnings;
}
private WithWarnings(
Object value, int maxWarnings, EconomicSet<Warning> warnings, Warning... additionalWarnings) {
this(value, maxWarnings, warnings, false, additionalWarnings);
}
private static boolean isAcceptableValue(Object value) {
assert value != null;
assert !(value instanceof WithWarnings) : "Trying to double wrap WithWarnings " + value;
boolean goodValue =
value instanceof TruffleObject
|| value instanceof Long
|| value instanceof Double
|| value instanceof Boolean;
assert goodValue : "Unexpected value floating around " + value + " type: " + value.getClass();
return goodValue;
}
public static WithWarnings wrap(EnsoContext ctx, Object value, Warning... warnings) {
if (value instanceof WithWarnings with) {
return with.append(ctx, warnings);
} else {
return new WithWarnings(value, ctx.getWarningsLimit(), warnings);
}
}
public Object getValue() {
return value;
}
public WithWarnings append(EnsoContext ctx, boolean limitReached, Warning... newWarnings) {
return new WithWarnings(value, ctx.getWarningsLimit(), warnings, limitReached, newWarnings);
}
public WithWarnings append(EnsoContext ctx, Warning... newWarnings) {
return new WithWarnings(value, ctx.getWarningsLimit(), warnings, newWarnings);
}
public WithWarnings append(EnsoContext ctx, ArrayRope<Warning> newWarnings) {
return new WithWarnings(
value, ctx.getWarningsLimit(), warnings, newWarnings.toArray(Warning[]::new));
}
// Ignore the warnings cache in .value and re-fetch them using the WarningsLibrary.
// This is only used for shouldWrap=true.
private Warning[] getWarningsNoCache(WarningsLibrary warningsLibrary) {
if (warningsLibrary != null && warningsLibrary.hasWarnings(value)) {
try {
return warningsLibrary.getWarnings(value, null, true);
} catch (UnsupportedMessageException e) {
throw EnsoContext.get(warningsLibrary).raiseAssertionPanic(warningsLibrary, null, e);
}
} else {
return Warning.fromSetToArray(warnings);
}
}
public Warning[] getWarningsArray(WarningsLibrary warningsLibrary, boolean shouldWrap) {
Warning[] allWarnings;
if (warningsLibrary != null && warningsLibrary.hasWarnings(value)) {
try {
Warning[] valueWarnings = warningsLibrary.getWarnings(value, null, shouldWrap);
EconomicSet<Warning> tmp = cloneSetAndAppend(maxWarnings, warnings, valueWarnings);
allWarnings = Warning.fromSetToArray(tmp);
} catch (UnsupportedMessageException e) {
throw EnsoContext.get(warningsLibrary).raiseAssertionPanic(warningsLibrary, null, e);
}
} else {
allWarnings = Warning.fromSetToArray(warnings);
}
return allWarnings;
}
/**
* @return the number of warnings.
*/
public int getWarningsCount() {
return warnings.size();
}
public ArrayRope<Warning> getReassignedWarningsAsRope(Node location, boolean shouldWrap) {
return new ArrayRope<>(getReassignedWarnings(location, shouldWrap, null));
}
public Warning[] getReassignedWarnings(
Node location, boolean shouldWrap, WarningsLibrary warningsLibrary) {
Warning[] warnings = getWarningsArray(warningsLibrary, shouldWrap);
for (int i = 0; i < warnings.length; i++) {
warnings[i] = warnings[i].reassign(location);
}
return warnings;
}
public static WithWarnings appendTo(EnsoContext ctx, Object target, ArrayRope<Warning> warnings) {
if (target instanceof WithWarnings) {
return ((WithWarnings) target).append(ctx, warnings.toArray(Warning[]::new));
} else {
return new WithWarnings(target, ctx.getWarningsLimit(), warnings.toArray(Warning[]::new));
}
}
public static WithWarnings appendTo(EnsoContext ctx, Object target, Warning... warnings) {
return appendTo(ctx, target, false, warnings);
}
public static WithWarnings appendTo(
EnsoContext ctx, Object target, boolean reachedMaxCount, Warning... warnings) {
if (target instanceof WithWarnings) {
return ((WithWarnings) target).append(ctx, reachedMaxCount, warnings);
} else {
return new WithWarnings(target, ctx.getWarningsLimit(), reachedMaxCount, warnings);
}
}
@CompilerDirectives.TruffleBoundary
private PanicException asException(Node where) {
var rawWarn = this.getWarnings(where, false, WarningsLibrary.getUncached());
var ctx = EnsoContext.get(where);
var scopeOfAny = ctx.getBuiltins().any().getDefinitionScope();
var toText = UnresolvedSymbol.build("to_text", scopeOfAny);
var node = InteropMethodCallNode.getUncached();
var state = State.create(ctx);
var text = Text.empty();
for (var w : rawWarn) {
try {
var wText = node.execute(toText, state, new Object[] {w});
if (wText instanceof Text t) {
text = text.add(t);
}
} catch (ArityException e) {
throw ctx.raiseAssertionPanic(where, null, e);
}
}
return new PanicException(text, where);
}
@ExportMessage
Object send(Message message, Object[] args, @CachedLibrary(limit = "3") ReflectionLibrary lib)
throws Exception {
return lib.send(value, message, args);
}
@ExportMessage
boolean hasWarnings() {
return true;
}
@ExportMessage
Warning[] getWarnings(
Node location,
boolean shouldWrap,
@Shared("warnsLib") @CachedLibrary(limit = "3") WarningsLibrary warningsLibrary) {
if (location != null) {
return getReassignedWarnings(location, shouldWrap, warningsLibrary);
} else {
if (shouldWrap) {
// In the wrapping case, we don't use the local cache in .values, since
// it contains unwrapped warnings. Instead, we fetch them again.
return getWarningsNoCache(warningsLibrary);
} else {
return Warning.fromSetToArray(warnings);
}
}
}
@ExportMessage
Object removeWarnings(@Shared("warnsLib") @CachedLibrary(limit = "3") WarningsLibrary warnings)
throws UnsupportedMessageException {
if (warnings.hasWarnings(value)) {
return warnings.removeWarnings(value);
} else {
return value;
}
}
@ExportMessage
public boolean isLimitReached() {
return limitReached;
}
@ExportMessage
boolean hasType(@Shared("typesLib") @CachedLibrary(limit = "3") TypesLibrary types) {
return types.hasType(value);
}
@ExportMessage
Type getType(@Shared("typesLib") @CachedLibrary(limit = "3") TypesLibrary types) {
return types.getType(value);
}
@ExportMessage
boolean hasSpecialDispatch() {
return true;
}
@ExportMessage
boolean isException() {
return true;
}
@ExportMessage
RuntimeException throwException(@Bind("$node") Node node) throws UnsupportedMessageException {
throw asException(node);
}
public static class WarningEquivalence extends Equivalence {
@Override
public boolean equals(Object a, Object b) {
if (a instanceof Warning thisObj && b instanceof Warning thatObj) {
return thisObj.getSequenceId() == thatObj.getSequenceId();
}
return false;
}
@Override
public int hashCode(Object o) {
return (int) ((Warning) o).getSequenceId();
}
}
@CompilerDirectives.TruffleBoundary
private EconomicSet<Warning> createSetFromArray(int maxWarnings, Warning[] entries) {
EconomicSet<Warning> set = EconomicSet.create(new WarningEquivalence());
for (int i = 0; i < entries.length; i++) {
if (set.size() == maxWarnings) {
return set;
}
set.add(entries[i]);
}
return set;
}
private EconomicSet<Warning> cloneSetAndAppend(
int maxWarnings, EconomicSet<Warning> initial, Warning[] entries) {
return initial.size() == maxWarnings
? initial
: cloneSetAndAppendSlow(maxWarnings, initial, entries);
}
@CompilerDirectives.TruffleBoundary
private EconomicSet<Warning> cloneSetAndAppendSlow(
int maxWarnings, EconomicSet<Warning> initial, Warning[] entries) {
EconomicSet<Warning> set = EconomicSet.create(new WarningEquivalence());
for (Warning warning : initial) {
if (set.size() == maxWarnings) {
return set;
}
set.add(warning);
}
for (int i = 0; i < entries.length; i++) {
if (set.size() == maxWarnings) {
return set;
}
set.add(entries[i]);
}
return set;
}
@Override
public String toString() {
return "WithWarnings{"
+ value
+ " has "
+ warnings.size()
+ " warnings"
+ (limitReached ? " (warnings limit reached)}" : "}");
}
}

View File

@ -20,8 +20,8 @@ import org.enso.interpreter.runtime.data.Type;
import org.enso.interpreter.runtime.error.DataflowError; import org.enso.interpreter.runtime.error.DataflowError;
import org.enso.interpreter.runtime.error.PanicException; import org.enso.interpreter.runtime.error.PanicException;
import org.enso.interpreter.runtime.error.PanicSentinel; import org.enso.interpreter.runtime.error.PanicSentinel;
import org.enso.interpreter.runtime.error.WithWarnings;
import org.enso.interpreter.runtime.number.EnsoBigInteger; import org.enso.interpreter.runtime.number.EnsoBigInteger;
import org.enso.interpreter.runtime.warning.WithWarnings;
@GenerateUncached @GenerateUncached
public abstract class TypeOfNode extends Node { public abstract class TypeOfNode extends Node {
@ -70,8 +70,8 @@ public abstract class TypeOfNode extends Node {
} }
@Specialization @Specialization
Object doWarning(WithWarnings value) { Object doWarning(WithWarnings value, @Cached TypeOfNode withoutWarning) {
return execute(value.getValue()); return withoutWarning.execute(value.getValue());
} }
static boolean isWithoutType(Object value, TypesLibrary types) { static boolean isWithoutType(Object value, TypesLibrary types) {

View File

@ -22,9 +22,11 @@ import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.error.DataflowError; import org.enso.interpreter.runtime.error.DataflowError;
import org.enso.interpreter.runtime.error.PanicException; import org.enso.interpreter.runtime.error.PanicException;
import org.enso.interpreter.runtime.error.PanicSentinel; import org.enso.interpreter.runtime.error.PanicSentinel;
import org.enso.interpreter.runtime.error.Warning;
import org.enso.interpreter.runtime.number.EnsoBigInteger; import org.enso.interpreter.runtime.number.EnsoBigInteger;
import org.enso.interpreter.runtime.scope.ModuleScope; import org.enso.interpreter.runtime.scope.ModuleScope;
import org.enso.interpreter.runtime.warning.Warning;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
import org.enso.interpreter.runtime.warning.WithWarnings;
import org.enso.polyglot.data.TypeGraph; import org.enso.polyglot.data.TypeGraph;
/** /**
@ -57,6 +59,8 @@ import org.enso.polyglot.data.TypeGraph;
PanicSentinel.class, PanicSentinel.class,
EnsoHashMap.class, EnsoHashMap.class,
Warning.class, Warning.class,
WithWarnings.class,
WarningsLibrary.class,
EnsoFile.class, EnsoFile.class,
EnsoDate.class, EnsoDate.class,
EnsoDateTime.class, EnsoDateTime.class,

View File

@ -0,0 +1,209 @@
package org.enso.interpreter.runtime.warning;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Cached.Shared;
import com.oracle.truffle.api.dsl.GenerateUncached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.InvalidArrayIndexException;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.profiles.ConditionProfile;
import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.data.hash.EnsoHashMap;
import org.enso.interpreter.runtime.data.hash.HashMapInsertAllNode;
import org.enso.interpreter.runtime.data.hash.HashMapInsertNode;
import org.enso.interpreter.runtime.data.hash.HashMapSizeNode;
import org.enso.interpreter.runtime.data.vector.ArrayLikeAtNode;
import org.enso.interpreter.runtime.data.vector.ArrayLikeLengthNode;
@GenerateUncached
public abstract class AppendWarningNode extends Node {
public static AppendWarningNode build() {
return AppendWarningNodeGen.create();
}
public static AppendWarningNode getUncached() {
return AppendWarningNodeGen.getUncached();
}
/**
* Appends a warning to the given object.
*
* @param object Object that will have the warning appended
* @param warnings Either an array-like object containing warnings to append, or a single warning.
* It is expected that all the elements in the container are of {@link Warning} class.
* @return A wrapped object with warnings
*/
public abstract WithWarnings executeAppend(VirtualFrame frame, Object object, Object warnings);
@Specialization
WithWarnings doSingleWarning(
VirtualFrame frame,
Object object,
Warning warning,
@Shared @Cached HashMapInsertNode mapInsertNode,
@Shared @Cached HashMapSizeNode mapSizeNode,
@Shared @Cached ConditionProfile isWithWarnsProfile) {
EnsoHashMap warnsMap;
int warnsLimit;
Object value;
if (isWithWarnsProfile.profile(object instanceof WithWarnings)) {
warnsMap = ((WithWarnings) object).warnings;
value = ((WithWarnings) object).value;
warnsLimit = ((WithWarnings) object).maxWarnings;
} else {
warnsMap = EnsoHashMap.empty();
value = object;
warnsLimit = EnsoContext.get(this).getWarningsLimit();
}
boolean isLimitReached;
if ((int) mapSizeNode.execute(warnsMap) < warnsLimit) {
warnsMap = mapInsertNode.execute(frame, warnsMap, warning.getSequenceId(), warning);
isLimitReached = false;
} else {
isLimitReached = true;
}
return new WithWarnings(value, warnsLimit, isLimitReached, warnsMap);
}
@Specialization
WithWarnings doMultipleWarningsArray(
VirtualFrame frame,
Object object,
Warning[] warnings,
@Shared @Cached HashMapInsertNode mapInsertNode,
@Shared @Cached HashMapSizeNode mapSizeNode,
@Shared @Cached ConditionProfile isWithWarnsProfile) {
EnsoHashMap warnsMap;
int warnsLimit;
Object value;
if (isWithWarnsProfile.profile(object instanceof WithWarnings)) {
warnsMap = ((WithWarnings) object).warnings;
warnsLimit = ((WithWarnings) object).maxWarnings;
value = ((WithWarnings) object).value;
} else {
warnsMap = EnsoHashMap.empty();
warnsLimit = EnsoContext.get(this).getWarningsLimit();
value = object;
}
var isLimitReached = false;
for (var warn : warnings) {
if (mapSizeNode.execute(warnsMap) < warnsLimit) {
warnsMap = mapInsertNode.execute(frame, warnsMap, warn.getSequenceId(), warn);
} else {
isLimitReached = true;
break;
}
}
return new WithWarnings(value, warnsLimit, isLimitReached, warnsMap);
}
/**
* This specialization should be the most frequent - just wrapping the given {@code object} with
* warnings hash map
*/
@Specialization(guards = {"!isWithWarns(object)"})
WithWarnings doObjectMultipleWarningsHashMap(
VirtualFrame frame,
Object object,
EnsoHashMap newWarnsMap,
@Shared @Cached HashMapSizeNode mapSizeNode) {
assert !(object instanceof WithWarnings);
int warnsLimit = EnsoContext.get(this).getWarningsLimit();
var limitReached = mapSizeNode.execute(newWarnsMap) >= warnsLimit;
return new WithWarnings(object, warnsLimit, limitReached, newWarnsMap);
}
@Specialization
WithWarnings doWithWarnMultipleWarningsHashMap(
VirtualFrame frame,
WithWarnings withWarnings,
EnsoHashMap newWarnsMap,
@Cached HashMapInsertAllNode mapInsertAllNode,
@Shared @Cached HashMapSizeNode mapSizeNode) {
if (withWarnings.isLimitReached()) {
return withWarnings;
}
var maxWarns = withWarnings.maxWarnings;
var warnsMap = withWarnings.warnings;
var curWarnsCnt = (int) mapSizeNode.execute(warnsMap);
warnsMap =
mapInsertAllNode.executeInsertAll(frame, warnsMap, newWarnsMap, maxWarns - curWarnsCnt);
var isLimitReached =
mapSizeNode.execute(withWarnings.warnings) + mapSizeNode.execute(newWarnsMap) >= maxWarns;
return new WithWarnings(withWarnings.value, withWarnings.maxWarnings, isLimitReached, warnsMap);
}
@Specialization(guards = {"interop.hasArrayElements(warnings)", "!isWarnArray(warnings)"})
WithWarnings doMultipleWarningsInterop(
VirtualFrame frame,
Object object,
Object warnings,
@CachedLibrary(limit = "3") InteropLibrary interop,
@Shared @Cached HashMapInsertNode mapInsertNode,
@Cached ArrayLikeAtNode atNode,
@Cached ArrayLikeLengthNode lengthNode,
@Shared @Cached HashMapSizeNode mapSizeNode,
@Shared @Cached ConditionProfile isWithWarnsProfile) {
assert !(warnings instanceof Warning[]);
EnsoHashMap warnsMap;
int warnsLimit;
Object value;
if (isWithWarnsProfile.profile(object instanceof WithWarnings)) {
value = ((WithWarnings) object).value;
warnsLimit = ((WithWarnings) object).maxWarnings;
warnsMap = ((WithWarnings) object).warnings;
} else {
value = object;
warnsLimit = EnsoContext.get(this).getWarningsLimit();
warnsMap = EnsoHashMap.empty();
}
var currWarnsCnt = mapSizeNode.execute(warnsMap);
var resWarningMap =
insertToWarningMap(
frame, warnsMap, warnings, warnsLimit, lengthNode, atNode, mapInsertNode, mapSizeNode);
var newWarnsCnt = lengthNode.executeLength(warnings);
var isLimitReached = currWarnsCnt + newWarnsCnt >= warnsLimit;
return new WithWarnings(value, warnsLimit, isLimitReached, resWarningMap);
}
/** Inserts all {@code warnings} to the {@code initialWarningMap}. */
private EnsoHashMap insertToWarningMap(
VirtualFrame frame,
EnsoHashMap initialWarningMap,
Object warnings,
int warnsLimit,
ArrayLikeLengthNode lengthNode,
ArrayLikeAtNode atNode,
HashMapInsertNode mapInsertNode,
HashMapSizeNode mapSizeNode) {
EnsoHashMap resWarningMap = initialWarningMap;
for (long i = 0; i < lengthNode.executeLength(warnings); i++) {
Warning warn;
try {
warn = (Warning) atNode.executeAt(warnings, i);
} catch (InvalidArrayIndexException e) {
throw CompilerDirectives.shouldNotReachHere(e);
} catch (ClassCastException e) {
throw EnsoContext.get(this).raiseAssertionPanic(this, "Expected warning object", e);
}
if (mapSizeNode.execute(resWarningMap) >= warnsLimit) {
return resWarningMap;
}
resWarningMap = mapInsertNode.execute(frame, resWarningMap, warn.getSequenceId(), warn);
}
return resWarningMap;
}
protected static boolean isWarnArray(Object obj) {
return obj instanceof Warning[];
}
protected static boolean isWithWarns(Object obj) {
return obj instanceof WithWarnings;
}
}

View File

@ -0,0 +1,70 @@
package org.enso.interpreter.runtime.warning;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Cached.Shared;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.nodes.Node;
import java.util.Arrays;
import java.util.Comparator;
import org.enso.interpreter.dsl.AcceptsWarning;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.data.hash.HashMapInsertAllNode;
import org.enso.interpreter.runtime.data.vector.ArrayLikeHelpers;
@BuiltinMethod(
type = "Warning",
name = "get_all_vector",
description = "Gets all the warnings associated with the value.",
autoRegister = false)
public abstract class GetAllWarningsNode extends Node {
public static GetAllWarningsNode build() {
return GetAllWarningsNodeGen.create();
}
public abstract Object execute(@AcceptsWarning Object value, boolean shouldWrap);
@Specialization
Object doWithWarn(
WithWarnings value,
boolean shouldWrap,
@Shared @CachedLibrary(limit = "3") WarningsLibrary warningsLib,
@Shared @CachedLibrary(limit = "3") InteropLibrary interop,
@Cached HashMapInsertAllNode mapInsertAllNode) {
var warns = value.getWarningsArray(shouldWrap, warningsLib, mapInsertAllNode, interop);
sortArray(warns);
return ArrayLikeHelpers.asVectorEnsoObjects(warns);
}
@Fallback
Object doGeneric(
Object value,
boolean shouldWrap,
@Shared @CachedLibrary(limit = "3") WarningsLibrary warningsLib,
@Shared @CachedLibrary(limit = "3") InteropLibrary interop) {
assert !(value instanceof WithWarnings);
if (warningsLib.hasWarnings(value)) {
try {
var warnsMap = warningsLib.getWarnings(value, shouldWrap);
var warnings = Warning.fromMapToArray(warnsMap, interop);
sortArray(warnings);
return ArrayLikeHelpers.asVectorEnsoObjects(warnings);
} catch (UnsupportedMessageException e) {
throw EnsoContext.get(warningsLib).raiseAssertionPanic(warningsLib, null, e);
}
} else {
return ArrayLikeHelpers.asVectorEmpty();
}
}
@TruffleBoundary
private static void sortArray(Warning[] arr) {
Arrays.sort(arr, Comparator.comparing(Warning::getSequenceId).reversed());
}
}

View File

@ -0,0 +1,32 @@
package org.enso.interpreter.runtime.warning;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.AcceptsWarning;
import org.enso.interpreter.dsl.BuiltinMethod;
@BuiltinMethod(
type = "Warning",
name = "limit_reached",
description =
"Returns `true` if the maximal number of warnings has been reached, `false` otherwise.",
autoRegister = false)
public abstract class LimitReachedNode extends Node {
public static LimitReachedNode build() {
return LimitReachedNodeGen.create();
}
public abstract boolean execute(@AcceptsWarning Object value);
@Specialization
boolean doWithWarns(WithWarnings withWarns) {
return withWarns.isLimitReached();
}
@Fallback
boolean doGeneric(Object value, @CachedLibrary(limit = "3") WarningsLibrary warnsLib) {
return warnsLib.hasWarnings(value) && warnsLib.isLimitReached(value);
}
}

View File

@ -0,0 +1,118 @@
package org.enso.interpreter.runtime.warning;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Cached.Shared;
import com.oracle.truffle.api.dsl.GenerateUncached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.InvalidArrayIndexException;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.profiles.ConditionProfile;
import org.enso.interpreter.dsl.AcceptsWarning;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.data.hash.EnsoHashMap;
import org.enso.interpreter.runtime.data.hash.HashMapInsertNode;
@BuiltinMethod(
type = "Warning",
name = "set_array",
description = "Sets all the warnings associated with the value.",
autoRegister = false)
@GenerateUncached
public abstract class SetWarningsNode extends Node {
public static SetWarningsNode build() {
return SetWarningsNodeGen.create();
}
public abstract Object execute(VirtualFrame frame, @AcceptsWarning Object value, Object warnings);
@Specialization(guards = "isEmpty(warnings, interop)")
Object doEmpty(
VirtualFrame frame,
Object object,
Object warnings,
@Shared @CachedLibrary(limit = "3") InteropLibrary interop,
@Shared @Cached ConditionProfile isWithWarnsProfile) {
if (isWithWarnsProfile.profile(object instanceof WithWarnings)) {
return ((WithWarnings) object).value;
} else {
return object;
}
}
@Specialization(guards = "!isEmpty(warnings, interop)")
Object doSetArray(
VirtualFrame frame,
Object object,
Warning[] warnings,
@Shared @CachedLibrary(limit = "3") InteropLibrary interop,
@Shared @Cached HashMapInsertNode mapInsertNode,
@Shared @Cached ConditionProfile isWithWarnsProfile) {
var warnMap = EnsoHashMap.empty();
for (var warn : warnings) {
warnMap = mapInsertNode.execute(frame, warnMap, warn.getSequenceId(), warn);
}
var maxWarns = EnsoContext.get(this).getWarningsLimit();
var isLimitReached = warnings.length >= maxWarns;
if (isWithWarnsProfile.profile(object instanceof WithWarnings)) {
return new WithWarnings(((WithWarnings) object).value, maxWarns, isLimitReached, warnMap);
} else {
return new WithWarnings(object, maxWarns, isLimitReached, warnMap);
}
}
@Specialization(guards = {"!isEmpty(warnings, interop)", "!isWarnArray(warnings)"})
Object doSetInteropArray(
VirtualFrame frame,
Object object,
Object warnings,
@Shared @CachedLibrary(limit = "3") InteropLibrary interop,
@Shared @Cached HashMapInsertNode mapInsertNode,
@Shared @Cached ConditionProfile isWithWarnsProfile) {
assert !(warnings instanceof Warning[]);
var warnMap = EnsoHashMap.empty();
var ctx = EnsoContext.get(this);
try {
var size = interop.getArraySize(warnings);
for (long i = 0; i < interop.getArraySize(warnings); i++) {
var warn = (Warning) interop.readArrayElement(warnings, i);
warnMap = mapInsertNode.execute(frame, warnMap, warn.getSequenceId(), warn);
}
var maxWarns = ctx.getWarningsLimit();
var isLimitReached = size >= maxWarns;
if (isWithWarnsProfile.profile(object instanceof WithWarnings)) {
return new WithWarnings(((WithWarnings) object).value, maxWarns, isLimitReached, warnMap);
} else {
return new WithWarnings(object, maxWarns, isLimitReached, warnMap);
}
} catch (UnsupportedMessageException | InvalidArrayIndexException e) {
throw CompilerDirectives.shouldNotReachHere(e);
} catch (ClassCastException e) {
throw ctx.raiseAssertionPanic(this, "Expected Warning, got something else", e);
}
}
protected static boolean isEmpty(Object warns, InteropLibrary interop) {
if (warns instanceof Warning[] warnsArray) {
return warnsArray.length == 0;
}
if (interop.hasArrayElements(warns)) {
try {
return interop.getArraySize(warns) == 0;
} catch (UnsupportedMessageException e) {
throw CompilerDirectives.shouldNotReachHere(e);
}
}
return false;
}
protected static boolean isWarnArray(Object obj) {
return obj instanceof Warning[];
}
}

View File

@ -0,0 +1,134 @@
package org.enso.interpreter.runtime.warning;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Bind;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.StopIterationException;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.Builtin;
import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.data.EnsoObject;
import org.enso.interpreter.runtime.data.Type;
import org.enso.interpreter.runtime.data.hash.EnsoHashMap;
import org.enso.interpreter.runtime.data.hash.HashMapInsertNode;
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
@Builtin(pkg = "error", stdlibName = "Standard.Base.Warning.Warning")
@ExportLibrary(TypesLibrary.class)
public final class Warning implements EnsoObject {
private final Object value;
private final Object origin;
private final long sequenceId;
private Warning(Object value, Object origin, long sequenceId) {
this.value = value;
this.origin = origin;
this.sequenceId = sequenceId;
}
@Builtin.Method(name = "value", description = "Gets the payload of the warning.")
@SuppressWarnings("generic-enso-builtin-type")
public Object getValue() {
return value;
}
@Builtin.Method(name = "origin", description = "Gets the payload of the warning.")
@SuppressWarnings("generic-enso-builtin-type")
public Object getOrigin() {
return origin;
}
@Builtin.Method(
name = "create",
description = "Creates a new instance of the primitive warning value.",
autoRegister = false)
@Builtin.Specialize
public static Warning create(EnsoContext ctx, Object payload, Object origin) {
return new Warning(payload, origin, ctx.nextSequenceId());
}
@Builtin.Method(
name = "attach_with_stacktrace",
description = "Attaches the given warning to the value.",
autoRegister = false)
@Builtin.Specialize
public static WithWarnings attach(
EnsoContext ctx,
Object value,
Object warning,
Object origin,
@Cached AppendWarningNode appendWarningNode) {
var warn = new Warning(warning, origin, ctx.nextSequenceId());
return appendWarningNode.executeAppend(null, value, warn);
}
/** Slow version of {@link #fromMapToArray(EnsoHashMap, InteropLibrary)}. */
@TruffleBoundary
public static Warning[] fromMapToArray(EnsoHashMap map) {
return fromMapToArray(map, InteropLibrary.getUncached());
}
public static Warning[] fromMapToArray(EnsoHashMap map, InteropLibrary interop) {
assert interop.hasHashEntries(map);
Warning[] warns = null;
try {
long mapSize = interop.getHashSize(map);
assert mapSize < Integer.MAX_VALUE;
warns = new Warning[(int) mapSize];
var hashValuesIt = interop.getHashValuesIterator(map);
assert interop.isIterator(hashValuesIt);
int warnsIdx = 0;
while (interop.hasIteratorNextElement(hashValuesIt)) {
var value = interop.getIteratorNextElement(hashValuesIt);
warns[warnsIdx] = (Warning) value;
warnsIdx++;
}
return warns;
} catch (UnsupportedMessageException | ClassCastException | ArrayIndexOutOfBoundsException e) {
throw CompilerDirectives.shouldNotReachHere(e);
} catch (StopIterationException e) {
assert warns != null;
return warns;
}
}
public static EnsoHashMap fromArrayToMap(Warning[] warnings, HashMapInsertNode mapInsertNode) {
var map = EnsoHashMap.empty();
for (var warn : warnings) {
map = mapInsertNode.execute(null, map, warn.getSequenceId(), warn);
}
return map;
}
@CompilerDirectives.TruffleBoundary
@Override
public String toString() {
return value.toString();
}
public long getSequenceId() {
return sequenceId;
}
@ExportMessage
boolean hasType() {
return true;
}
@ExportMessage
Type getType(@Bind("$node") Node node) {
return EnsoContext.get(node).getBuiltins().warning();
}
public static Warning wrapMapError(WarningsLibrary warningsLib, Warning warning, long index) {
var ctx = EnsoContext.get(warningsLib);
var error = warning.getValue();
var wrappedError = ctx.getBuiltins().error().makeMapError(index, error);
return Warning.create(ctx, wrappedError, warning.getOrigin());
}
}

View File

@ -1,10 +1,10 @@
package org.enso.interpreter.runtime.error; package org.enso.interpreter.runtime.warning;
import com.oracle.truffle.api.interop.UnsupportedMessageException; import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.library.GenerateLibrary; import com.oracle.truffle.api.library.GenerateLibrary;
import com.oracle.truffle.api.library.Library; import com.oracle.truffle.api.library.Library;
import com.oracle.truffle.api.library.LibraryFactory; import com.oracle.truffle.api.library.LibraryFactory;
import com.oracle.truffle.api.nodes.Node; import org.enso.interpreter.runtime.data.hash.EnsoHashMap;
@GenerateLibrary @GenerateLibrary
public abstract class WarningsLibrary extends Library { public abstract class WarningsLibrary extends Library {
@ -44,15 +44,14 @@ public abstract class WarningsLibrary extends Library {
/** /**
* Returns all unique warnings associated with the receiver. * Returns all unique warnings associated with the receiver.
* *
* @param receiver the receiver to analyze * @param receiver the receiver to get the warnings from
* @param location optional parameter specifying the node to which the warnings should be
* reassigned to
* @param shouldWrap if true, warnings attached to elements in array-likes are wrapped in * @param shouldWrap if true, warnings attached to elements in array-likes are wrapped in
* Map_Error * Map_Error
* @return the associated warnings * @return the associated warnings as map - keys are {@code sequenceId} and values are warnings.
* Not null
*/ */
@GenerateLibrary.Abstract(ifExported = {"hasWarnings"}) @GenerateLibrary.Abstract(ifExported = {"hasWarnings"})
public Warning[] getWarnings(Object receiver, Node location, boolean shouldWrap) public EnsoHashMap getWarnings(Object receiver, boolean shouldWrap)
throws UnsupportedMessageException { throws UnsupportedMessageException {
throw UnsupportedMessageException.create(); throw UnsupportedMessageException.create();
} }

View File

@ -0,0 +1,282 @@
package org.enso.interpreter.runtime.warning;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Bind;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Cached.Shared;
import com.oracle.truffle.api.interop.ArityException;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage;
import com.oracle.truffle.api.library.Message;
import com.oracle.truffle.api.library.ReflectionLibrary;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.node.callable.InteropMethodCallNode;
import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
import org.enso.interpreter.runtime.data.EnsoObject;
import org.enso.interpreter.runtime.data.Type;
import org.enso.interpreter.runtime.data.hash.EnsoHashMap;
import org.enso.interpreter.runtime.data.hash.HashMapInsertAllNode;
import org.enso.interpreter.runtime.data.hash.HashMapInsertNodeGen;
import org.enso.interpreter.runtime.data.hash.HashMapSizeNode;
import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.data.vector.ArrayLikeAtNode;
import org.enso.interpreter.runtime.data.vector.ArrayLikeAtNodeGen;
import org.enso.interpreter.runtime.data.vector.ArrayLikeLengthNode;
import org.enso.interpreter.runtime.data.vector.ArrayLikeLengthNodeGen;
import org.enso.interpreter.runtime.error.PanicException;
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
import org.enso.interpreter.runtime.state.State;
/**
* Represents a typical Enso <em>value with warnings</em>. As much of care as possible is taken to
* delegate all operations to the underlaying {@code value}. Warnings are considered {@link
* InteropLibrary#isException exceptional values} - e.g. one can check for them in Java polyglot
* code as:
*
* <pre>
* Value value = ...;
* if (value.fitsInLong() && value.isException()) {
* // probably an Integer with a warning
* try {
* warningMulti.throwException();
* } catch (PolyglotException ex) {
* System.out.println("Warnings attached to " + value.asLong() + " are " + ex.getMessage());
* }
* }
* </pre>
*/
@ExportLibrary(TypesLibrary.class)
@ExportLibrary(WarningsLibrary.class)
@ExportLibrary(ReflectionLibrary.class)
@ExportLibrary(value = InteropLibrary.class, delegateTo = "value")
public final class WithWarnings implements EnsoObject {
final Object value;
/**
* Internal storage for warnings is a PE-friendly hash map. The key is a sequence ID (gathered
* from {@link EnsoContext#nextSequenceId()} and the value is the warning itself. Note that it is
* essential that sequenceId is the key so that the warnings are not duplicated.
*/
final EnsoHashMap warnings;
private final boolean limitReached;
final int maxWarnings;
/**
* Creates a new instance of value wrapped in warnings.
*
* @param value value to be wrapped in warnings
* @param maxWarnings maximal number of warnings allowed to be attached to the value
* @param warningsMap warnings originally attached to a value
* @param limitReached if `true`, indicates that `warnings` have already been limited for a
* custom-method, `false` otherwise
*/
WithWarnings(Object value, int maxWarnings, boolean limitReached, EnsoHashMap warningsMap) {
assert isAcceptableValue(value);
var mapSizeNode = HashMapSizeNode.getUncached();
assert mapSizeNode.execute(warningsMap) <= maxWarnings;
if (limitReached) {
assert mapSizeNode.execute(warningsMap) == maxWarnings;
}
this.value = value;
this.maxWarnings = maxWarnings;
this.limitReached = limitReached || mapSizeNode.execute(warningsMap) >= maxWarnings;
this.warnings = warningsMap;
}
/**
* Explicit creation of WithWarnings. Allows to set a specific {@code maxWarnings} count, which
* cannot be achieved by using warning-handling nodes like {@link AppendWarningNode}. If {@code
* maxWarnings} does not need to be set explicitly, use nodes to create WithWarning objects
* instead.
*
* @param value value to be wrapped in warnings
* @param maxWarnings maximal number of warnings allowed to be attached to the value
* @param limitReached if `true`, no other warnings will be attached to the {@code value}.
* @param warnings array of warnings to be attached to the {@code value}
*/
public static WithWarnings create(
Object value, int maxWarnings, boolean limitReached, Warning[] warnings) {
assert warnings.length <= maxWarnings;
if (limitReached) {
assert warnings.length == maxWarnings;
}
var warnMap = Warning.fromArrayToMap(warnings, HashMapInsertNodeGen.getUncached());
return new WithWarnings(value, maxWarnings, limitReached, warnMap);
}
private static boolean isAcceptableValue(Object value) {
assert value != null;
assert !(value instanceof WithWarnings) : "Trying to double wrap WithWarnings " + value;
boolean goodValue =
value instanceof TruffleObject
|| value instanceof Long
|| value instanceof Double
|| value instanceof Boolean;
assert goodValue : "Unexpected value floating around " + value + " type: " + value.getClass();
return goodValue;
}
public Object getValue() {
return value;
}
// Ignore the warnings cache in .value and re-fetch them using the WarningsLibrary.
// This is only used for shouldWrap=true.
private EnsoHashMap getWarningsNoCache(
WarningsLibrary warningsLibrary, ArrayLikeLengthNode lengthNode, ArrayLikeAtNode atNode) {
assert warningsLibrary != null;
if (warningsLibrary.hasWarnings(value)) {
try {
return warningsLibrary.getWarnings(value, true);
} catch (UnsupportedMessageException e) {
throw EnsoContext.get(warningsLibrary).raiseAssertionPanic(warningsLibrary, null, e);
}
} else {
return warnings;
}
}
/**
* Slow version of {@link #getWarningsArray(boolean, WarningsLibrary, HashMapInsertAllNode,
* InteropLibrary)} that uses uncached version of nodes and libraries parameters.
*/
public Warning[] getWarningsArray(boolean shouldWrap) {
return getWarningsArray(
shouldWrap,
WarningsLibrary.getUncached(),
HashMapInsertAllNode.getUncached(),
InteropLibrary.getUncached());
}
public Warning[] getWarningsArray(
boolean shouldWrap,
WarningsLibrary warningsLibrary,
HashMapInsertAllNode mapInsertAllNode,
InteropLibrary interop) {
Warning[] allWarnsArray;
if (warningsLibrary != null && warningsLibrary.hasWarnings(value)) {
try {
var valueWarnings = warningsLibrary.getWarnings(value, shouldWrap);
var allWarns =
mapInsertAllNode.executeInsertAll(null, warnings, valueWarnings, maxWarnings);
allWarnsArray = Warning.fromMapToArray(allWarns, interop);
} catch (UnsupportedMessageException e) {
throw EnsoContext.get(warningsLibrary).raiseAssertionPanic(warningsLibrary, null, e);
}
} else {
allWarnsArray = Warning.fromMapToArray(warnings, interop);
}
return allWarnsArray;
}
@CompilerDirectives.TruffleBoundary
private PanicException asException(Node where) {
var warnsMap =
this.getWarnings(
false,
WarningsLibrary.getUncached(),
ArrayLikeAtNodeGen.getUncached(),
ArrayLikeLengthNodeGen.getUncached());
var warns = Warning.fromMapToArray(warnsMap);
var ctx = EnsoContext.get(where);
var scopeOfAny = ctx.getBuiltins().any().getDefinitionScope();
var toText = UnresolvedSymbol.build("to_text", scopeOfAny);
var node = InteropMethodCallNode.getUncached();
var state = State.create(ctx);
var text = Text.empty();
for (var w : warns) {
try {
var wText = node.execute(toText, state, new Object[] {w});
if (wText instanceof Text t) {
text = text.add(t);
}
} catch (ArityException e) {
throw ctx.raiseAssertionPanic(where, null, e);
}
}
return new PanicException(text, where);
}
@ExportMessage
Object send(Message message, Object[] args, @CachedLibrary(limit = "3") ReflectionLibrary lib)
throws Exception {
return lib.send(value, message, args);
}
@ExportMessage
boolean hasWarnings() {
return true;
}
@ExportMessage
EnsoHashMap getWarnings(
boolean shouldWrap,
@Shared("warnsLib") @CachedLibrary(limit = "3") WarningsLibrary warningsLibrary,
@Cached ArrayLikeAtNode atNode,
@Cached ArrayLikeLengthNode lengthNode) {
if (shouldWrap) {
// In the wrapping case, we don't use the local cache in .values, since
// it contains unwrapped warnings. Instead, we fetch them again.
return getWarningsNoCache(warningsLibrary, lengthNode, atNode);
} else {
return warnings;
}
}
@ExportMessage
Object removeWarnings(@Shared("warnsLib") @CachedLibrary(limit = "3") WarningsLibrary warnings)
throws UnsupportedMessageException {
if (warnings.hasWarnings(value)) {
return warnings.removeWarnings(value);
} else {
return value;
}
}
@ExportMessage
public boolean isLimitReached() {
return limitReached;
}
@ExportMessage
boolean hasType(@Shared("typesLib") @CachedLibrary(limit = "3") TypesLibrary types) {
return types.hasType(value);
}
@ExportMessage
Type getType(@Shared("typesLib") @CachedLibrary(limit = "3") TypesLibrary types) {
return types.getType(value);
}
@ExportMessage
boolean hasSpecialDispatch() {
return true;
}
@ExportMessage
boolean isException() {
return true;
}
@ExportMessage
RuntimeException throwException(@Bind("$node") Node node) {
throw asException(node);
}
@Override
public String toString() {
return "WithWarnings{"
+ value
+ " has "
+ HashMapSizeNode.getUncached().execute(warnings)
+ " warnings"
+ (limitReached ? " (warnings limit reached)}" : "}");
}
}

View File

@ -5,7 +5,11 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
/** An interface marking an argument as allowing it to accept a WithWarnings. */ /**
* An interface marking an argument as allowing it to accept a WithWarnings. Parameters annotated
* with this annotation will not have their warnings stripped, so if it is of type {@link
* org.enso.interpreter.runtime.warning.WithWarnings} it will not be unwrapped.
*/
@Target(ElementType.PARAMETER) @Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
public @interface AcceptsWarning {} public @interface AcceptsWarning {}

View File

@ -16,7 +16,7 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
public @interface BuiltinMethod { public @interface BuiltinMethod {
/** /**
* @return the language-level type of {@code self} argument. * @return the language-level type of {@code self} argument. Not a fully-qualified name.
*/ */
String type(); String type();

View File

@ -130,6 +130,8 @@ public class MethodProcessor
"com.oracle.truffle.api.CompilerDirectives", "com.oracle.truffle.api.CompilerDirectives",
"com.oracle.truffle.api.dsl.UnsupportedSpecializationException", "com.oracle.truffle.api.dsl.UnsupportedSpecializationException",
"com.oracle.truffle.api.frame.VirtualFrame", "com.oracle.truffle.api.frame.VirtualFrame",
"com.oracle.truffle.api.interop.InteropLibrary",
"com.oracle.truffle.api.interop.UnsupportedMessageException",
"com.oracle.truffle.api.nodes.ControlFlowException", "com.oracle.truffle.api.nodes.ControlFlowException",
"com.oracle.truffle.api.nodes.Node", "com.oracle.truffle.api.nodes.Node",
"com.oracle.truffle.api.nodes.NodeInfo", "com.oracle.truffle.api.nodes.NodeInfo",
@ -146,14 +148,18 @@ public class MethodProcessor
"org.enso.interpreter.runtime.callable.function.FunctionSchema", "org.enso.interpreter.runtime.callable.function.FunctionSchema",
"org.enso.interpreter.runtime.EnsoContext", "org.enso.interpreter.runtime.EnsoContext",
"org.enso.interpreter.runtime.builtin.Builtins", "org.enso.interpreter.runtime.builtin.Builtins",
"org.enso.interpreter.runtime.data.ArrayRope", "org.enso.interpreter.runtime.data.hash.EnsoHashMap",
"org.enso.interpreter.runtime.data.hash.HashMapInsertNode",
"org.enso.interpreter.runtime.data.hash.HashMapInsertAllNode",
"org.enso.interpreter.runtime.data.text.Text", "org.enso.interpreter.runtime.data.text.Text",
"org.enso.interpreter.runtime.error.DataflowError", "org.enso.interpreter.runtime.error.DataflowError",
"org.enso.interpreter.runtime.error.PanicException", "org.enso.interpreter.runtime.error.PanicException",
"org.enso.interpreter.runtime.error.Warning",
"org.enso.interpreter.runtime.error.WithWarnings",
"org.enso.interpreter.runtime.state.State", "org.enso.interpreter.runtime.state.State",
"org.enso.interpreter.runtime.type.TypesGen"); "org.enso.interpreter.runtime.type.TypesGen",
"org.enso.interpreter.runtime.warning.Warning",
"org.enso.interpreter.runtime.warning.WarningsLibrary",
"org.enso.interpreter.runtime.warning.WithWarnings",
"org.enso.interpreter.runtime.warning.AppendWarningNode");
/** List of exception types that should be caught from the builtin's execute method. */ /** List of exception types that should be caught from the builtin's execute method. */
private static final List<String> handleExceptionTypes = private static final List<String> handleExceptionTypes =
@ -190,6 +196,13 @@ public class MethodProcessor
+ " extends BuiltinRootNode implements InlineableNode.Root {"); + " extends BuiltinRootNode implements InlineableNode.Root {");
} }
out.println(" private @Child " + methodDefinition.getOriginalClassName() + " bodyNode;"); out.println(" private @Child " + methodDefinition.getOriginalClassName() + " bodyNode;");
out.println(
" private @Child AppendWarningNode appendWarningNode = AppendWarningNode.build();");
out.println(
" private @Child WarningsLibrary warnLib ="
+ " WarningsLibrary.getFactory().createDispatched(5);");
out.println(
" private @Child HashMapInsertAllNode mapInsertAllNode = HashMapInsertAllNode.build();");
out.println(); out.println();
out.println(" private static final class Internals {"); out.println(" private static final class Internals {");
out.println(" Internals(boolean s) {"); out.println(" Internals(boolean s) {");
@ -282,27 +295,44 @@ public class MethodProcessor
+ " body = " + " body = "
+ methodDefinition.getConstructorExpression() + methodDefinition.getConstructorExpression()
+ ";"); + ";");
out.println(
" private @Child AppendWarningNode appendWarningNode ="
+ " AppendWarningNode.build();");
out.println(
" private @Child WarningsLibrary warnLib ="
+ " WarningsLibrary.getFactory().createDispatched(5);");
out.println(
" private @Child HashMapInsertAllNode mapInsertAllNode ="
+ " HashMapInsertAllNode.build();");
out.println();
out.println(" @Override"); out.println(" @Override");
out.println(" public Object call(VirtualFrame frame, Object[] args) {"); out.println(" public Object call(VirtualFrame frame, Object[] args) {");
out.println(" return handleExecute(frame, extra, body, args);"); out.println(
" return handleExecute(frame, extra, body, appendWarningNode, warnLib,"
+ " mapInsertAllNode, args);");
out.println(" }"); out.println(" }");
out.println(" }"); out.println(" }");
out.println();
out.println(" return new Inlineable();"); out.println(" return new Inlineable();");
out.println(" }"); out.println(" }");
} }
out.println();
out.println(" @Override"); out.println(" @Override");
out.println(" public Object execute(VirtualFrame frame) {"); out.println(" public Object execute(VirtualFrame frame) {");
if (methodDefinition.needsFrame()) { if (methodDefinition.needsFrame()) {
out.println(" var args = frame.getArguments();"); out.println(" var args = frame.getArguments();");
} else { } else {
out.println( out.println(
" return handleExecute(frame, this.internals, bodyNode, frame.getArguments());"); " return handleExecute(frame, this.internals, bodyNode, this.appendWarningNode,"
+ " this.warnLib, this.mapInsertAllNode, frame.getArguments());");
out.println(" }"); out.println(" }");
out.println( out.println(
" private static Object handleExecute(VirtualFrame frame, Internals internals, " " private static Object handleExecute(VirtualFrame frame, Internals internals, "
+ methodDefinition.getOriginalClassName() + methodDefinition.getOriginalClassName()
+ " bodyNode, Object[] args) {"); + " bodyNode, AppendWarningNode appendWarningNode, WarningsLibrary warnLib,"
+ " HashMapInsertAllNode mapInsertAllNode, Object[] args) {");
} }
out.println(" var prefix = internals.staticOfInstanceMethod ? 1 : 0;"); out.println(" var prefix = internals.staticOfInstanceMethod ? 1 : 0;");
out.println(" State state = Function.ArgumentsHelper.getState(args);"); out.println(" State state = Function.ArgumentsHelper.getState(args);");
@ -346,8 +376,8 @@ public class MethodProcessor
out.println(" internals.anyWarningsProfile.enter();"); out.println(" internals.anyWarningsProfile.enter();");
out.println(" Object result;"); out.println(" Object result;");
out.println(wrapInTryCatch("result = " + executeCall + ";", 6)); out.println(wrapInTryCatch("result = " + executeCall + ";", 6));
out.println(" EnsoContext ctx = EnsoContext.get(bodyNode);"); out.println(
out.println(" return WithWarnings.appendTo(ctx, result, gatheredWarnings);"); " return appendWarningNode.executeAppend(frame, result, gatheredWarnings);");
out.println(" } else {"); out.println(" } else {");
out.println(wrapInTryCatch("return " + executeCall + ";", 6)); out.println(wrapInTryCatch("return " + executeCall + ";", 6));
out.println(" }"); out.println(" }");
@ -598,28 +628,27 @@ public class MethodProcessor
return false; return false;
} else { } else {
out.println(" boolean anyWarnings = false;"); out.println(" boolean anyWarnings = false;");
out.println(" ArrayRope<Warning> gatheredWarnings = new ArrayRope<>();"); out.println(" int maxWarnings = EnsoContext.get(bodyNode).getWarningsLimit();");
out.println(" EnsoHashMap gatheredWarnings = EnsoHashMap.empty();");
for (var arg : argsToCheck) { for (var arg : argsToCheck) {
String argCode = arrayRead(argumentsArray, arg.getPosition());
out.println( out.println(
" if (" " if ("
+ arrayRead(argumentsArray, arg.getPosition()) + arrayRead(argumentsArray, arg.getPosition())
+ " instanceof WithWarnings) {"); + " instanceof WithWarnings withWarnings) {");
out.println( out.println(
" internals." + mkArgumentInternalVarName(arg) + WARNING_PROFILE + ".enter();"); " internals." + mkArgumentInternalVarName(arg) + WARNING_PROFILE + ".enter();");
out.println(" anyWarnings = true;"); out.println(" anyWarnings = true;");
out.println(" try {"); // begin try
out.println(" var warns = warnLib.getWarnings(withWarnings, false);");
out.println(" " + argCode + " = withWarnings.getValue();");
out.println( out.println(
" WithWarnings withWarnings = (WithWarnings) " " gatheredWarnings = mapInsertAllNode.executeInsertAll(frame, gatheredWarnings,"
+ arrayRead(argumentsArray, arg.getPosition()) + " warns, maxWarnings);");
+ ";"); out.println(" } catch (UnsupportedMessageException e) {"); // end try
out.println( out.println(" throw CompilerDirectives.shouldNotReachHere(e);");
" " out.println(" }"); // end catch
+ arrayRead(argumentsArray, arg.getPosition()) out.println(" }"); // end hasWarnings
+ " = withWarnings.getValue();");
out.print(
"""
gatheredWarnings = gatheredWarnings.prepend(withWarnings.getReassignedWarningsAsRope(bodyNode, false));
""");
out.println(" }");
} }
return true; return true;
} }

View File

@ -122,7 +122,7 @@ class CachedLibraryMethodParameter extends SpecializedMethodParameter {
public static final String INTEROP_LIBRARY = "com.oracle.truffle.api.interop.InteropLibrary"; public static final String INTEROP_LIBRARY = "com.oracle.truffle.api.interop.InteropLibrary";
public static final String WARNINGS_LIBRARY = public static final String WARNINGS_LIBRARY =
"org.enso.interpreter.runtime.error.WarningsLibrary"; "org.enso.interpreter.runtime.warning.WarningsLibrary";
} }
class InjectedMethodParameter extends SpecializedMethodParameter { class InjectedMethodParameter extends SpecializedMethodParameter {

View File

@ -14,6 +14,7 @@ import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier; import javax.lang.model.element.Modifier;
import javax.lang.model.element.VariableElement; import javax.lang.model.element.VariableElement;
import javax.tools.Diagnostic; import javax.tools.Diagnostic;
import javax.tools.Diagnostic.Kind;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.enso.interpreter.dsl.AcceptsWarning; import org.enso.interpreter.dsl.AcceptsWarning;
import org.enso.interpreter.dsl.Builtin; import org.enso.interpreter.dsl.Builtin;
@ -160,12 +161,16 @@ public final class SpecializedMethodsGenerator extends MethodGenerator {
paramss.forEach( paramss.forEach(
(k, v) -> { (k, v) -> {
if (v.size() != elements.size()) { if (v.size() != elements.size()) {
throw new RuntimeException( processingEnv
"Restriction: Specialized methods have to have equal number of parameters.\n" .getMessager()
+ "Expected " .printMessage(
+ elements.size() Kind.ERROR,
+ ", got " "Restriction: Specialized methods have to have equal number of parameters.\n"
+ v.size()); + "Expected "
+ elements.size()
+ ", got "
+ v.size(),
elements.get(0));
} }
MethodParameter p = v.get(0); MethodParameter p = v.get(0);

View File

@ -46,16 +46,6 @@ get_foo x = x.foo
unwrap x = Integer.from x unwrap x = Integer.from x
reassign_test x =
consed = Wrap.Value x
reconsed = rewrap consed
i = unwrap reconsed
rereconsed = Wrap.Value i
x1 = get_foo rereconsed
prim_sum = 1 + x1
r = poly_sum prim_sum 1
r
baz value = Warning.attach value "I have warned you" baz value = Warning.attach value "I have warned you"
bar value = baz value bar value = baz value
foo value = bar value foo value = bar value
@ -165,21 +155,6 @@ add_specs suite_builder = suite_builder.group "Dataflow Warnings" group_builder-
relevant = warning_stack . drop (..Last current.length) relevant = warning_stack . drop (..Last current.length)
relevant.map .name . should_equal (['baz', 'bar', 'foo'].map ('Warnings_Spec.'+)) relevant.map .name . should_equal (['baz', 'bar', 'foo'].map ('Warnings_Spec.'+))
group_builder.specify "should attach reassignment info in the last-reassigned-first order" <|
x = Warning.attach "warn!" 1
r = reassign_test x
warn = Warning.get_all r . first
reassignments = warn.reassignments.map .name
expected_stack = []
+ [ 'Warnings_Spec.poly_sum' ]
+ [ 'Integer.+' ]
+ [ 'Warnings_Spec.get_foo' ]
+ [ 'Wrap.Value' ]
+ [ 'Integer.from' ]
+ [ 'Warnings_Spec.rewrap' ]
+ [ 'Wrap.Value' ]
reassignments.should_equal expected_stack.to_vector
group_builder.specify "should allow to set all warnings" <| group_builder.specify "should allow to set all warnings" <|
warned = Warning.attach 1 <| Warning.attach 2 <| Warning.attach 3 <| Warning.attach 4 "foo" warned = Warning.attach 1 <| Warning.attach 2 <| Warning.attach 3 <| Warning.attach 4 "foo"
warnings = Warning.get_all warned warnings = Warning.get_all warned