Skip to content

Compatibility with julia nightly #10

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

Open
wants to merge 26 commits into
base: main
Choose a base branch
from
Open

Conversation

mlechu
Copy link

@mlechu mlechu commented Apr 2, 2025

The goal of this change is to have JuliaLowering work on julia nightly again,
particularly for the JETLS development that's going on, by porting recent
changes from julia-syntax.scm. It will be an unfortunately large diff, but a
smaller PR would leave JuliaLowering broken for all versions of julia, so I've
settled on keeping the commit history readable.

Major changes

  • The CodeInfo and Core.Binding structs have been changed
  • Raw symbols from lowering are no longer resolved to globals; wrap them in
    globalref
  • Use new IR forms:
  • New isdefinedglobal builtin
  • signature changes to ::GeneratedFunctionStub and Core._typebody!
  • Hook for testing JuliaLowering in Core once the changes on julia's end are merged; @activate JuliaLowering

TODO

These can be done in this PR or a later one.

Commits considered

This list was generated so I could keep track of my work, but it might be helpful here to list bugfixes we don't have yet. Note that it only includes changes to julia-syntax.scm, which doesn't cover all breaking commits. N/A means I intentionally skipped it for some reason (e.g. already done before this PR).

Long list
  • [N/A] b7e72322b3 Don't let setglobal! implicitly create bindings (#54678)
  • [N/A] 1c59231b2d opaque_closure: Allow opting out of PartialOpaque (#54734)
  • 1cd47c3094 lowering: Remove outerref intermediate form (#54772)
  • 67c9989e83 Fix lowering for export and similar (#54812)
  • dfd1d490d5 lowering: Refactor lowering for const and typed globals (#54773)
  • 696d9c3e69 Follow up #54772 - don't accidentally put Module into method name slot (#54856)
  • [N/A] a5f0016008 Support @opaque Tuple{T,U...}->RT (...)->... syntax for explicit arg/return types (#54947)
  • aa0758594f lowering: Don't resolve type bindings earlier than necessary (#54999)
  • 4f1af7f39d Allow opting out of PartialOpaque support via Expr(:opaque_closure, ...) (#55068)
  • [N/A] 0c9c1e25b7 Canonicalize names of nested functions by keeping a more fine grained counter -- per (module, method name) pair (#53719)
  • 2616634a17 fix #45494, error in ssa conversion with complex type decl (#55744)
  • [N/A] 911e02558d better error for esc outside of macro expansion (#55797)
  • dc344285d5 Fix type instability of closures capturing types (2) (#40985)
  • 6de6b46b7e lowering: split finally blocks for exceptional control-flow (#55876)
  • 435516da3a Undo the decision to publish incomplete types to the binding table (#56497)
  • 505907bd11 Add lowering and interpreter support for :latestworld (#56523)
  • deac82ad91 lowering: don't reverse handler order in (pop-handler-list ...) (#55871)
  • 034e6093c5 Make world-age increments explicit (#56509)
  • f1b0b010dd Fix scope of hoisted signature-local variables (#56712)
  • 2c87290f2e lowering: Canonicalize to builtins for global assignment (#56713)
  • 3a68b035cc Fully outline all GlobalRefs (#56746)
  • 3d85309e80 Consolidate various isdefined functionality into a new builtin (#56985)
  • 30177d0578 cleanup: Remove fallback post-lowering global resolution (#57051)
  • 7f99e95377 bpart: Start enforcing minimum world age for const bparts (#57102)
  • f209eba244 bpart: Start enforcing min_world for global variable definitions (#57150)
  • 888cf03506 bpart: Fully switch to partitioned semantics (#57253)
  • e553fc8c6f Add missing latestworld after parameterized type alias (#57299)
  • 512eb5e2c9 lowering: Only try to define the method once (#57346)
  • 91e733384e internals: add _defaultctor function for defining ctors
  • 414aca21e9 lowering: Don't mutate lambda in linearize (#57416)
  • 3c02af98dc lowering: Handle malformed ... expressions (#57480)
  • 2e57730aa2 lowering: Allow chaining of >: in where (#57554)
  • 7fa0c13b93 Make no-body function declaration implicitly global (#57562)
  • ca17927311 lowering: Don't closure-convert in import or using (#57774)
  • 6043569ffd Factor out expand-table '= lambda into expand-assignment
  • 9a437a52d3 const lowering: respect scope, prohibit non-const const assignment
  • 64672f5e8a const lowering: resolve scopes under assign-const-if-global (#57470)
  • 3360a44a4d Disallow non-lhs all-underscore variable names (#57626)
  • 1570bed513 Align :method Expr return value between interpreter and codegen (#58076)

mlechu added 2 commits March 28, 2025 08:28
This used to implicitly refer to a module-level name, but lowering is now
expected to wrap it in a `globalref`. Part of JuliaLang/julia#54772
mlechu added 15 commits April 17, 2025 14:43
JuliaLang/julia#54773, JuliaLang/julia#56713, JuliaLang/julia#57470. Some
changes omitted from `expand-decls` and `expand-assignment`.
JuliaLang/julia#56746.  Also call :slot and :static_parameter valid (for now)
Part of JuliaLang/julia#56497.  `insert-struct-shim` (for the a self-reference
case) is omitted for now.
For method defs, `latestworld` is produced in desugaring rather than closure
conversion for now (our closure conversion doesn't seem to cover the same
cases as lisp lowering yet).

Covers JuliaLang/julia#56523, JuliaLang/julia#56509, JuliaLang/julia#57299.

Also includes changes from JuliaLang/julia#57102 (bpart: Start enforcing minimum
world age for const bparts) and JuliaLang/julia#57150 (bpart: Start enforcing
min_world for global variable definitions) since the lowering changes from those
appear to be amendments to the changes above.
`Core._typebody!` now takes a new "prev" argument, which we don't use yet here.
 Changes from JuliaLang/julia#57253
Fix segfaulting test.  Thanks for the TODO
Was missing increments after closure type declarations and a kwarg function
declaration. Fixes the world age warnings when running tests.  Part of
JuliaLang/julia#56509
Differentiate it from AST K"const"
As of JuliaLang/julia#57765, `jl_module_public` is no longer exported.  Change
our runtime to handle it like `public` and `export` like we handle `import`
or `using` for now
I believe this was a world age issue
Adapt to Core.Binding changes in JuliaLang/julia#54788.  Also remove
     `is_defined_nothrow_global` (JuliaLang/julia#56746)
Part of JuliaLang/julia#56497 that I ignored before.  I have doubts about how
    long this solution will stay in the base repository, and how complete it
    is (doesn't seem to work with M1.M2.S), but we are testing for it here.

Also change the expected value of a test changed in the same PR.
Changes from JuliaLang/julia#57253 (bpart: Fully switch to partitioned
     semantics).  This fixes one failing test and realigns struct desugaring to
     match lisp for now.

Also changed: the expected result of redefining a primitive type (now allowed).
@mlechu mlechu marked this pull request as ready for review April 18, 2025 17:02
@mlechu
Copy link
Author

mlechu commented Apr 18, 2025

All tests (excluding IR equality tests) are passing, so I'm marking this as ready for review!

@mlechu mlechu changed the title WIP: Compatibility with julia nightly Compatibility with julia nightly Apr 18, 2025
mlechu added 2 commits April 22, 2025 10:31
Once the corresponding bindings are merged into julia, `@activate JuliaLowering`
     will set both `Core._parse` and `Core._lower` so that parsing produces our
     `SyntaxTree`s and we become the default lowerer.

The parser hook copied from JuliaSyntax is unfortunate, but I think moving this
     hook there and keeping it in sync would be a bigger headache while
     JuliaLowering is still under development.
mlechu added a commit to JuliaLang/JuliaSyntax.jl that referenced this pull request Apr 23, 2025
Moved to [JuliaLowering](c42f/JuliaLowering.jl#10)
     instead (the data structure we need is defined there).
mlechu added a commit to JuliaLang/JuliaSyntax.jl that referenced this pull request Apr 23, 2025
Moved to [JuliaLowering](c42f/JuliaLowering.jl#10)
     instead (the data structure we need is defined there).
@c42f
Copy link
Owner

c42f commented Apr 29, 2025

This is pretty epic, thanks!

Doing this port was going to be my first task back to get me back into the swing of things 😆

Regarding the TODOs - definitely best to do them later, except for the tests. We should keep the IR tests working.

I'm keen on moving to the use of utility functions like _defaultctors where possible to keep the IR cleaner and more compact. In fact JuliaLowering has several such runtime functions in src/runtime.jl already. For example, eval_closure_type() is almost entirely analogous. This can be done in a separate PR though as it reduces churn in the IR tests.

I think we should have K"constdecl" or some such instead of having const be semantically ambiguous. We can translate that to the runtime's const-with-nested-GlobalRef form at the last stage of lowering.

@mlechu
Copy link
Author

mlechu commented Apr 29, 2025

We should keep the IR tests working.

Sure, will update them

I think we should have K"constdecl" or some such instead of having const be semantically ambiguous. We can translate that to the runtime's const-with-nested-GlobalRef form at the last stage of lowering.

Already done (albeit buried), see aa32c09. I didn't do this at first because I thought both const forms were the same 🙃. Note that this form is probably getting eliminated once JuliaLang/julia#58187 is merged.

@c42f
Copy link
Owner

c42f commented Apr 30, 2025

Already done

Amazing, thanks! I did see this as soon as I started to do an in depth review.

For the tests, you can use the utility function in test/util.jl refresh_all_ir_tests() I think it's called.

In addition to existing tests, please add new tests if there's new features to cover. I imagine latestworld could do with some tests to cover the specific conditions like explicit include() - I remember there was some slightly hacky conditions keno was forced to add for compat?

Can you explain the wrap stuff a bit? I vaguely recall deciding with @JeffBezanson that we would try to avoid going to heroic lengths to deal with this edge case, which iirc is arguably a miss-feature of Julia's syntax. But I can't remember the details, it could have been something else!

@c42f
Copy link
Owner

c42f commented Apr 30, 2025

Generally, I'll note that I'm taking the occasional liberty with compatibility when I really feel some obscure feature is poorly motivated in modern Julia. Especially when it's an "emergent feature" of the implementation, rather than something done purposefully.

There's not many such cases and it's still always a really hard call, but I feel a brand new implementation does provide a little extra freedom here, especially when the code complexity of implementing a mis-feature is high!

@mlechu
Copy link
Author

mlechu commented Apr 30, 2025

please add new tests if there's new features to cover

Will do. Once we can use JuliaLowering in core JuliaLang/julia#58207, we can also try using some of the lowering tests from the julia repository without huge manual porting effort.

I vaguely recall deciding with @ JeffBezanson that we would try to avoid going to heroic lengths to deal with this edge case

I'm pretty happy to delete wrap haha. I think I overestimated the level of compatibility with lisp lowering you were going for. I agree this is a good opportunity to let old emergent behaviour go.

mlechu added 4 commits April 30, 2025 13:06
As far as I'm aware, all this plumbing was just to support `const a,b,c = 1,2,3`
    having `b` and `c` inherit the `const`.
Fix `let; const x = 1; end`, which should throw.  Moves the check up to scope
    analysis (lisp has it in `compile`).
Too many to count.  Also fix `latestworld` printing.
@mlechu
Copy link
Author

mlechu commented May 1, 2025

OK, this is ready for more review. wrap turned out to be necessary for bringing const into destructuring assignments, so I chose to make const [tuple] = ... throw a LoweringError over having it silently drop the const for now—it's probably worth exploring other ways of making that work

topolarity added a commit to JuliaLang/julia that referenced this pull request May 7, 2025
This PR along with [the associated JuliaLowering
PR](c42f/JuliaLowering.jl#10) allow us to
experiment
with JuliaLowering as the core lowering implementation, mainly for JETLS
development at the moment (cc @aviatesk, @c42f).

## Changes

JuliaLowering works with `JuliaLowering.SyntaxTree` input instead of
Expr. This
change allows`SyntaxTree`s out of parsing and into lowering, hopefully
without
disturbing existing code expecting Expr.  The current design wraps
`x::SyntaxTree` like `Expr(:syntaxtree, x)` because otherwise
`toplevel_eval_flex` wouldn't know what `x` is, but once we're past the
experimental phase, it would only make sense for the compiler to
actually know
about the types it's operating on.

### New: `Core._lower`

It (a julia entrypoint), `jl_lower` (C entrypoint), `jl_fl_lower`, and
`fl_lower` all mirror the existing parsing API which was added in
#35243.


<details>

<summary>
Click for pre-PR call graph
</summary>


![graph(6)](https://github.com/user-attachments/assets/a993eb92-5668-4ee7-837f-801feb686cb4)

</details>

After this change:


![graph(7)](https://github.com/user-attachments/assets/697b93cf-b3df-47b2-bc0e-c358fb0a6dca)

Like `Core._parse`, `Core._lower` returns an `svec` with the first
element as
the expected result, and the remaining elements can hold extra
information like
the lowered SyntaxTree with provenance info (I've left the specification
of
extra return values for later, when JuliaLowering is more stable and
JETLS knows
what it needs, because we may opt to pack provenance into the lowered
CodeInfo
object instead). Parsing only uses the svec to return an offset, but it
could
actually make use of the the flexibility to return diagnostics in the
future.

It's unfortunately not clear what types we can expect to go into and
come out of
lowering (I got Expr, LineNumberNode, String, Symbol, and Nothing in
testing.)

### Remove `jl_lower_expr_mod`

I realize this isn't being called from C, and Core._lower is a more
sensible
entry point from julia. We could probably also remove some uncalled
parse and
jl_load functions.

## Limitations

These belong to JuliaLowering, but are listed here as known issues for
anyone
who wants to try it out.

- `(module ...)` forms don't go through JuliaLowering yet. Modules are
lowered
to a call to `JuliaLowering.eval_module(parentmod, modname,
syntaxtree)`,
which is currently a stub function that converts to Expr for lowering
and
  evaluation, which JuliaLowering can't use.
- Macros work differently.  Defining and calling macros should work, but
  invoking normal macros defined pre-JuliaLowering will not.
- Compilation is currently slow (~30 seconds for me).
@mlechu
Copy link
Author

mlechu commented May 15, 2025

A couple of opaque closure and generated function tests are re-broken on nightly, lol. I will take a look.

Edit: Generated function IR tests just need to stay in sync with JuliaSyntax changes (I'm using JuliaLang/JuliaSyntax.jl#552, which hasn't been released). Opaque closures hit assertion failures in jl_make_opaque_closure_method, but work most of the time on non-debug builds somehow.

@Keno
Copy link

Keno commented May 21, 2025

If this gets us closer to master can we merge this and then work on the remaining points after? I'd like to start playing with this so that we can have a concrete plan for 1.13.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants