Skip to content

Conversation

@JaroslavTulach
Copy link
Member

@JaroslavTulach JaroslavTulach commented Nov 18, 2025

Pull Request Description

Greatly improving "stepping over" for if_then_else and case_of control flow constructs. The condition and the branches are no longer treated as separate functions. Thus step over takes one into the selected branch rather than over. Also removing the need to step into method body. This fix probably also solves the problem with stepping into a function where the breakpoint was incorrectly placed at the beginning of a function comment (highlighted by the red box).

Checklist

Please ensure that the following checklist has been satisfied before submitting the PR:

  • Screenshots/screencasts have been attached, if there are any visual changes. For interactive or animated visual changes, a screencast is preferred.
  • All code follows the
    Java,
  • Unit tests have been written where possible.

@JaroslavTulach
Copy link
Member Author

  • @kazcw, I am not really happy about using fun.getBody()
  • body may not include function arguments, right?
  • however I haven't found any better element to anchor to
  • and possibly body is good enough for debugger

@JaroslavTulach
Copy link
Member Author

JaroslavTulach commented Nov 20, 2025

  • I am getting test failures like
sbt:enso> runtime-integration-tests/testOnly *NonStrictModeTests
[error] Test org.enso.interpreter.test.NonStrictModeTests.testAmbiguousConversion failed: java.lang.AssertionError: Cannot find Unnamed:7:1: error: Ambiguous conversion: Foo.from Bar is defined multiple times in this module. in enso.org.enso.compiler.Compiler
[error] lis 20, 2025 3:37:44 ODP. enso.org.enso.compiler.Compiler
[error] WARNING: Unnamed:7:23: error: Ambiguous conversion: Foo.from Bar is defined multiple times in this module.
[error]     7 | Foo.from (that:Bar) = Foo.Mk_Foo that.x+1000
[error]       |                       ^~~~~~~~~~~~~~~~~~~~~~
[error] , took 0.252 sec
[error]     at org.enso.interpreter.test.MockLogHandler.assertMessage(MockLogHandler.java:46)
[error]     at org.enso.interpreter.test.NonStrictModeTests.testAmbiguousConversion(NonStrictModeTests.java:51)
[error]     at jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
[error]     at java.lang.reflect.Method.invoke(Method.java:565)
[error]     ...
[error] Test org.enso.interpreter.test.NonStrictModeTests.testAmbiguousConversionUsage failed: java.lang.AssertionError: Cannot find Unnamed:9:1: error: Ambiguous conversion: Foo.from Bar is defined multiple times in this module. in enso.org.enso.compiler.Compiler
[error] lis 20, 2025 3:37:47 ODP. enso.org.enso.compiler.Compiler
[error] WARNING: Unnamed:9:23: error: Ambiguous conversion: Foo.from Bar is defined multiple times in this module.
[error]     9 | Foo.from (that:Bar) = Foo.Mk_Foo that.x+1000
[error]       |                       ^~~~~~~~~~~~~~~~~~~~~~
[error] , took 3.089 sec
[error]     at org.enso.interpreter.test.MockLogHandler.assertMessage(MockLogHandler.java:46)
[error]     at org.enso.interpreter.test.NonStrictModeTests.testAmbiguousConversionUsage(NonStrictModeTests.java:80)
[error]     at jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
[error]     at java.lang.reflect.Method.invoke(Method.java:565)
[error]     ...
[info] Test run org.enso.interpreter.test.NonStrictModeTests finished: 2 failed, 0 ignored, 3 total, 4.435s
[error] Failed: Total 3, Failed 2, Errors 0, Passed 1
[error] Failed tests:
[error]         org.enso.interpreter.test.NonStrictModeTests
[error] (runtime-integration-tests / Test / testOnly) sbt.TestsFailedException: Tests unsuccessful
[error] Total time: 38 s, completed 20. 11. 2025 15:37:47
  • I believe the test wants the whole function to be reported, not just the body
  • this test is fixed by 1209119

@JaroslavTulach JaroslavTulach added the CI: No changelog needed Do not require a changelog entry for this PR. label Nov 21, 2025
@JaroslavTulach
Copy link
Member Author

Another problem in IGV. The root cause is the same. Comment shouldn't be part of executable location:

obrazek

private IdentifiedLocation getIdentifiedLocation(
Tree.Function fn, int b, int e, Option<UUID> someId) {
var orig = getIdentifiedLocation((Tree) fn, b, e, someId);
var start = castToInt(fn.getName().getStartCode()) + b;
Copy link
Member Author

@JaroslavTulach JaroslavTulach Nov 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not really happy about the tricks that I have to do to remove comment from the location of Tree.Function, but unless @kazcw suggest easier way to do the same, I go with this code. (Multi line) comment just cannot be part of executable IdentifiedLocation as also demonstrated by the IGV problem.

@JaroslavTulach JaroslavTulach changed the title Debugging painful as comment is treated as part of function Extract only executable location from Tree.Function to simplify debugging & co. Nov 21, 2025
@kazcw
Copy link
Contributor

kazcw commented Nov 24, 2025

  • I am getting test failures like
sbt:enso> runtime-integration-tests/testOnly *NonStrictModeTests
[error] Test org.enso.interpreter.test.NonStrictModeTests.testAmbiguousConversion failed: java.lang.AssertionError: Cannot find Unnamed:7:1: error: Ambiguous conversion: Foo.from Bar is defined multiple times in this module. in enso.org.enso.compiler.Compiler
[error] lis 20, 2025 3:37:44 ODP. enso.org.enso.compiler.Compiler
[error] WARNING: Unnamed:7:23: error: Ambiguous conversion: Foo.from Bar is defined multiple times in this module.
[error]     7 | Foo.from (that:Bar) = Foo.Mk_Foo that.x+1000
[error]       |                       ^~~~~~~~~~~~~~~~~~~~~~
[error] , took 0.252 sec
[error]     at org.enso.interpreter.test.MockLogHandler.assertMessage(MockLogHandler.java:46)
[error]     at org.enso.interpreter.test.NonStrictModeTests.testAmbiguousConversion(NonStrictModeTests.java:51)
[error]     at jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
[error]     at java.lang.reflect.Method.invoke(Method.java:565)
[error]     ...
[error] Test org.enso.interpreter.test.NonStrictModeTests.testAmbiguousConversionUsage failed: java.lang.AssertionError: Cannot find Unnamed:9:1: error: Ambiguous conversion: Foo.from Bar is defined multiple times in this module. in enso.org.enso.compiler.Compiler
[error] lis 20, 2025 3:37:47 ODP. enso.org.enso.compiler.Compiler
[error] WARNING: Unnamed:9:23: error: Ambiguous conversion: Foo.from Bar is defined multiple times in this module.
[error]     9 | Foo.from (that:Bar) = Foo.Mk_Foo that.x+1000
[error]       |                       ^~~~~~~~~~~~~~~~~~~~~~
[error] , took 3.089 sec
[error]     at org.enso.interpreter.test.MockLogHandler.assertMessage(MockLogHandler.java:46)
[error]     at org.enso.interpreter.test.NonStrictModeTests.testAmbiguousConversionUsage(NonStrictModeTests.java:80)
[error]     at jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
[error]     at java.lang.reflect.Method.invoke(Method.java:565)
[error]     ...
[info] Test run org.enso.interpreter.test.NonStrictModeTests finished: 2 failed, 0 ignored, 3 total, 4.435s
[error] Failed: Total 3, Failed 2, Errors 0, Passed 1
[error] Failed tests:
[error]         org.enso.interpreter.test.NonStrictModeTests
[error] (runtime-integration-tests / Test / testOnly) sbt.TestsFailedException: Tests unsuccessful
[error] Total time: 38 s, completed 20. 11. 2025 15:37:47
* I believe the test wants the **whole function** to be reported, not just the body

* this test is fixed by [1209119](https://github.com/enso-org/enso/commit/12091190a20369b15cd5938ff41b36265bbbadc6)

The test was more correct before. The error does not originate from the body, it depends only on the name and arguments.

Copy link
Member

@Akirathan Akirathan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Testing: I think that it would be better, and more correct, to test this behavior in DebuggingEnsoTest. You can, e.g., get a stack trace and see if the source section of a function contains the documentation.

Implementation: I don't like that the location of a function is changed in TreeToIr. This feels dangerous. Why don't you just modify the places that are responsible for communicating with the debugger? Maybe ExpressionNode.getSourceSection?

@JaroslavTulach
Copy link
Member Author

JaroslavTulach commented Nov 24, 2025

Testing: I think that it would be better, and more correct, to test this behavior in DebuggingEnsoTest.

OK, I give it a try. Update on Dec 3:

  • yes, it is way better to have a test for the expected behavior
  • created new "control flow" test with the goal to provide reasonable step over behavior for if/then/else and case of control flow structures
  • check 355cb1db7bd7594e4e0f4221d58fa8267b9cec0f, @Akirathan

Implementation: I don't like that the location of a function is changed in TreeToIr. This feels dangerous. Why don't you just modify the places that are responsible for communicating with the debugger? Maybe ExpressionNode.getSourceSection?

No, that would be weird. Locations set in TreeToIr and just represented as SourceSection in the Truffle node. This would be a first case when SourceSection is different to location.

@JaroslavTulach JaroslavTulach changed the title Extract only executable location from Tree.Function to simplify debugging & co. Improving debugging "step over" experience Dec 3, 2025
@JaroslavTulach
Copy link
Member Author

JaroslavTulach commented Dec 3, 2025

  • changing locations of Tree.Function so that frontend and backend don't agree on the locations is dangerous
  • removing in 908290e for now
  • the debugging experience should be improved by the other changes in this PR
  • the problem with function comments still manifests itself
    • when reporting errors (demonstrated by the NonStrictModeTests) removed in 908290e
    • as well as while using IGV
  • stripping of comments from the the Location of a Tree.Function is still going to be needed
  • the safest approach seems to me to split Tree.Function into two parts:
    • Tree.Function = Tree.FunctionComment & Tree.FunctionDefinition
    • location of Tree.FunctionDefinition would then be exactly what's needed instead of 908290e

*/
private static Queue<SuspendedCallback> createStepOverEvents(int numSteps) {
Queue<SuspendedCallback> steps = new ArrayDeque<>();
steps.add((event) -> event.prepareStepInto(1));
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Take a look @Akirathan, removal of this line means the debugger will behave well more naturally!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How so? If numSteps is 1, an empty Queue is returned. How is that more "natural"?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • these are supposed to be step over steps
  • is it natural that first such a step is step into?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these are supposed to be step over steps

Right, my mistake.

is it natural that first such a step is step into?

Not at all. I have misread it.

closures.contains("factorial::factorial::fac"));
assertTrue(
"Name with dots for local method: " + closures, closures.contains("factorial.fac.acc"));
assertTrue("if/then/else: " + closures, closures.contains("if_then_else"));
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JaroslavTulach JaroslavTulach requested a review from 4e6 December 3, 2025 15:29
Copy link
Member

@Akirathan Akirathan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

few minor comments

*/
private static Queue<SuspendedCallback> createStepOverEvents(int numSteps) {
Queue<SuspendedCallback> steps = new ArrayDeque<>();
steps.add((event) -> event.prepareStepInto(1));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How so? If numSteps is 1, an empty Queue is returned. How is that more "natural"?

Co-authored-by: Pavel Marek <pavel.marek@enso.org>
@JaroslavTulach JaroslavTulach added the CI: Ready to merge This PR is eligible for automatic merge label Dec 3, 2025
@mergify mergify bot merged commit 1b2abdf into develop Dec 3, 2025
77 of 78 checks passed
@mergify mergify bot deleted the wip/jtulach/AvoidComment branch December 3, 2025 22:46
@enso-bot enso-bot bot mentioned this pull request Dec 5, 2025
3 tasks
@jdunkerley jdunkerley added this to the 2025.3 Release milestone Dec 5, 2025
jdunkerley pushed a commit that referenced this pull request Dec 5, 2025
Greatly improving _"stepping over"_ for `if_then_else` and `case_of` control flow constructs. The condition and the branches are no longer treated as separate functions. Thus _step over_ takes one into the selected branch rather than over. Also removing the need to _step into_ method body. This fix probably also solves the problem with stepping into a function where the breakpoint was incorrectly placed at the beginning of a function comment ([highlighted by the red box](https://github.com/user-attachments/assets/db244cc3-e578-4127-b8f4-7b1c3c9d408c)).

(cherry picked from commit 1b2abdf)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CI: No changelog needed Do not require a changelog entry for this PR. CI: Ready to merge This PR is eligible for automatic merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants