Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Infer method calls on known types and warn about nonexistent methods #11399

Merged
merged 171 commits into from
Jan 8, 2025

Conversation

radeusgd
Copy link
Member

@radeusgd radeusgd commented Oct 24, 2024

Pull Request Description

  • Implements Inferring types of method calls on Atoms #9812
  • Allows us to propagate type inference through method calls, at least in a basic way.
  • Adds a 'lint warning': when calling a method on an object of a known specific (non-Any) type that does not exist on that type, a warning is reported, because such a call would always result in No_Such_Method error at runtime.
    • Any has special behaviour - if x : Any we don't know what x might be, so we allow all methods on it and defer to the runtime to see if they will be valid or not.
    • This check is not proving correctness, but instead it's only triggering for 'provably wrong' situations.
  • Includes changes from Simplify BindingsMap by instead relying on StaticModuleScope for atom type resolution #11955:
    • removing obsolete AtomTypeInterface,
    • simplifying TypeRepresentation to be a plain data structure,
    • and removing a cycle from IR in BindingsMap in favour of relying on StaticModuleScope.

Important Notes

Checklist

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

  • The documentation has been updated, if necessary.
  • 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
    Scala,
    Java,
    TypeScript,
    and
    Rust
    style guides. In case you are using a language not listed above, follow the Rust style guide.
  • Unit tests have been written where possible.
  • If meaningful changes were made to logic or tests affecting Enso Cloud integration in the libraries,
    or the Snowflake database integration, a run of the Extra Tests has been scheduled.
    • If applicable, it is suggested to paste a link to a successful run of the Extra Tests.

…ce tests

Ideally, the static analysis tests should be runnable without the Graal runtime context. We'd like to be able to run these static passes without Graal runtime, e.g. inside of code editors. But refactoring import resolution and PackageRepository to work without Graal is out of scope of the current types work. It can be planned for later, when needed. For now the focus is to make type checking provide actual value - before that any editor integration wouldn't be useful anyway.
@radeusgd
Copy link
Member Author

I think I tried addressing all of the comments. I will very much appreciate your reviews on this next iteration of the PR. Sorry it is happening after such a long time - I will try to schedule time to iterate on this PR faster this time.

@Akirathan suggested to remove his `validateConsistency` check, but I've found I could fix the asserts by caching the returned value in persisted Map (if there was no caching, then on every ir.passData().get(...) a new instance of metadata was recreated, so the `validateConsistency` was failing because it requires object _identity_ and not only equality).

We can either keep this 'cache' or remove Pavel's check.
# Conflicts:
#	engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala
@radeusgd
Copy link
Member Author

radeusgd commented Jan 2, 2025

I'm getting weird test failures in ModuleManagementTest, removing the assertion as @Akirathan suggested has changed the error but it is still failing at deserialization in a weird way:

[info] - should allow importing literal-source modules *** FAILED *** (1 second, 195 milliseconds)
[info]   org.graalvm.polyglot.PolyglotException: java.lang.ArrayIndexOutOfBoundsException: Index 158052 out of bounds for length 1
[info]   at org.enso.persistance/org.enso.persist.PerInputImpl$InputCache.getRef(PerInputImpl.java:351)
[info]   at org.enso.persistance/org.enso.persist.PerInputImpl.findReference(PerInputImpl.java:58)
[info]   at org.enso.persistance/org.enso.persist.PerReferencePeristance.readObject(PerReferencePersistance.java:33)
[info]   at org.enso.persistance/org.enso.persist.PerReferencePeristance.readObject(PerReferencePersistance.java:8)
[info]   at org.enso.persistance/org.enso.persist.Persistance.readWith(Persistance.java:162)
[info]   at org.enso.persistance/org.enso.persist.PerInputImpl.readInline(PerInputImpl.java:67)
[info]   at org.enso.runtime/org.enso.interpreter.caches.PersistBindingsMap_Argument.readObject(PersistBindingsMap_Argument.java:14)
[info]   at org.enso.runtime/org.enso.interpreter.caches.PersistBindingsMap_Argument.readObject(PersistBindingsMap_Argument.java:4)
[info]   at org.enso.persistance/org.enso.persist.Persistance.readWith(Persistance.java:162)
[info]   at org.enso.persistance/org.enso.persist.PerInputImpl.readIndirect(PerInputImpl.java:218)
[info]   ...

Interestingly, it seems that my followup PR #11955 that removes the BindingsMap -> Expression cycle from the IR seems to not have these failures.

@JaroslavTulach do you think we can merge both PRs at once (I need to finish the followup but I'm working on that) OR would you like to help me investigating this serialization issue? Seems like there is still some issue in the persistance when cycles are around.

IMO it will be easier to just merge the followup, although we can also try investigating the persistance to make it more robust as such failures probably shouldn't be happening - but I don't think fixing persistance should be the focus of this PR in more extent than necesary.

…s (no parameter type mismatch checks yet, instead testing method mismatch)
@radeusgd
Copy link
Member Author

radeusgd commented Jan 2, 2025

I wanted to keep these PRs separate but it initially (I'm waiting for full CI suite to pass to verify) seems that the followup PR #11955 is fixing the persistance issues that are causing test failures in this PR. So I think ideally I'd merge #11955 into this PR and then merge the whole changeset as a single change. I'm not merging yet so that you can review more easily - first my answers to your requests in this PR and then the followup PR that builds on top of it.

Copy link
Member

@JaroslavTulach JaroslavTulach left a comment

Choose a reason for hiding this comment

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

I believe the abstraction of algorithms into singletonizers and their usage is correct. I don't feel completely qualified to talk about the static analysis, but I left few comments inline. If the CI is green, then I am for integrating these changes.

* @param useGlobalCacheLocations whether or not the compilation result should
* be written to the global cache
* @return future to track subsequent serialization of the library
*/
def compile(
shouldCompileDependencies: Boolean,
shouldWriteCache: Boolean,
Copy link
Member

Choose a reason for hiding this comment

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

I assume this is a reasonable way to avoid the static type information to be stored in the caches.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, plus IIRC it fixes a weird behaviour - that even if caches were disabled, they would still be written after compilation or something.

Now, if we run --no-ir-cache, we neither read nor write the caches, which I think is correct.

Copy link
Member

Choose a reason for hiding this comment

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

The reason why we are seeing failures like https://github.com/enso-org/enso/actions/runs/12680614808/job/35342759884?pr=12023#step:7:2875 is probably because of this nor write behavior. Previously there was one failing run and the bad caches would get overwritten. Now the bad caches stick there forever.

That's rational, but looks like the

throw new AssertionError("Unexpected output (errors?) from the compiler: " + out.toString());

may not be ready for that!

Copy link
Member Author

Choose a reason for hiding this comment

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

Oh...

Can't we just run the engine with --compile to write the new caches, before running this bench?

*
* <p>Ideally, the tests for the static analysis capabilities of the compiler should _not_ depend
* on the Graal runtime context, as they should be runnable in other contexts - i.e. in a Visual
* Studio Code language server.
Copy link
Member

Choose a reason for hiding this comment

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

That would indeed be ideal! The integration of the runtime-compiler into VSCode is blocked right now because we have no way to run the runtime-compiler without TruffleCompilerContext right now. If only these tests could be moved to runtime-compiler project test directory! Then:

  • they wouldn't depend on TruffleCompilerContext
  • they would need an independent compiler context implementation
  • such an implementation could be used in VSCode as well

I remember @Akirathan was once dreaming about using runtime-compiler in VSCode and this was a huge "known unknown".

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes I think ideally we should move them there - it seems like the right direction.

But implementing the independent compiler context is definitely a task for a separate PR.

@radeusgd
Copy link
Member Author

radeusgd commented Jan 7, 2025

In 6b0ce25 I merged the followup PR so that we can merge them both to develop in one try.

I will proceed with merging once CI gets green.

@radeusgd
Copy link
Member Author

radeusgd commented Jan 8, 2025

I've ran the benchmarks and tried comparing against develop:
benchs.zip

It's really quite hard to tell in each benchmark if any deviations are just due to benchmark instability or real regression (how are you guys managing to live without seeing the standard deviation error bars on the measurements? 😮).

Here are a few 'more concerning' results I saw:
image
image
image

Still they don't seem bad, so I think I will proceed with merging.

@radeusgd radeusgd added the CI: Ready to merge This PR is eligible for automatic merge label Jan 8, 2025
@mergify mergify bot merged commit 787372e into develop Jan 8, 2025
51 checks passed
@mergify mergify bot deleted the wip/radeusgd/9812-infer-method-calls branch January 8, 2025 15:41
radeusgd added a commit that referenced this pull request Jan 8, 2025
mergify bot pushed a commit that referenced this pull request Jan 8, 2025
@radeusgd radeusgd mentioned this pull request Jan 8, 2025
5 tasks
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.

Inferring types of method calls on Atoms
5 participants