Skip to content

Comments

Make ast types covariant over the allocator lifetime.#2943

Merged
Boshen merged 9 commits intooxc-project:mainfrom
branchseer:lifetime_variance
Apr 12, 2024
Merged

Make ast types covariant over the allocator lifetime.#2943
Boshen merged 9 commits intooxc-project:mainfrom
branchseer:lifetime_variance

Conversation

@branchseer
Copy link
Contributor

@branchseer branchseer commented Apr 11, 2024

Why

Due to the usage of &'alloc mut T in oxc_allocator::Box, and bumpalo::collections::Vec in oxc_allocator::Vec, ast types are currently invariant over their allocator lifetime 'a. This prevents ouroboros from generating borrow_* on ast type fields, leading to the unfriendly with_* api:

ast.with_ast(|bumpalo_program| {
println!("AST span: {:?}", bumpalo_program.0.span);
});

How

  • For oxc_allocator::Vec, switch to allocator_api2::vec::Vec, which has a covariant relationship with the allocator lifetime.
  • For oxc_allocator::Box, use std::ptr::NonNull which is specifically designed to be covariant. I don't use allocator_api2::boxed::Box because it holds the allocator for dropping, so the size is bigger.

Downside

Now that oxc_allocator::Box uses the unsafe NonNull. It has to be a private field to be safe. This make it impossible to do Box(....) pattern matching.

@github-actions github-actions bot added A-linter Area - Linter A-parser Area - Parser A-semantic Area - Semantic A-minifier Area - Minifier A-ast Area - AST A-codegen Area - Code Generation A-prettier labels Apr 11, 2024
@codspeed-hq
Copy link

codspeed-hq bot commented Apr 11, 2024

CodSpeed Performance Report

Merging #2943 will not alter performance

Comparing branchseer:lifetime_variance (3938ab0) with main (c250b28)

Summary

✅ 30 untouched benchmarks

@Boshen
Copy link
Member

Boshen commented Apr 12, 2024

Are you working on something that requires borrow_* from ouroboros, or is this a soundness change?

I'm asking because I don't understand this topic, and need to know more about the context of this change.

@branchseer
Copy link
Contributor Author

Are you working on something that requires borrow_* from ouroboros, or is this a soundness change?

I'm asking because I don't understand this topic, and need to know more about the context of this change.

@Boshen Thank you for your time reviewing.

I was working on a side project using oxc and bumped1 into this restriction. I just think borrow_* would be nice to have, although it's not essential for my case and I don't think this is a soundness change.

Also on other day I saw rolldown is also creating self-referencing ast types, so maybe this can make their life a little easier as well.

I am no expert to unsafe rust. But seeing both std's and allocator_api2's Box and Vec are lifetime covariant gives me some confidence about this change.

Footnotes

  1. pun intended

@Boshen
Copy link
Member

Boshen commented Apr 12, 2024

Thank you for working on this.

Merging because tests, rustc and miri all passed.

@Boshen Boshen merged commit f159f60 into oxc-project:main Apr 12, 2024
@zakarumych
Copy link

zakarumych commented Apr 12, 2024

&mut T is invariant over T because it is borrowed and when borrow ends T would be observed in it original form.
If T could be coerced to shorter lifetime and overwritten, original value would contain value with shorter lifetime than type's.

Example.

fn break_it<'a, 'b: 'a>(a: &mut &'b Vec<i32>, b: &'a Vec<i32>) {
  let coerced_a: &mut &'a Vec<i32> = a; // Would be valid if `&mut T` covariant over `T`.
  *coerced_a = b;
}

let x = vec![1, 2, 3];
let mut y: &Vec<i32> = &x;
break_it(&mut y, &vec![]);
println!("{y:?}"); // use after free 💥

This is not an issue with Box because it is owned value, you can't observe its content when Box over type with shortened lifetime drops.

fn break_it<'a, 'b: 'a>(a: Box<&'b Vec<i32>>, b: &'a Vec<i32>) {
  let coerced_a: Box<&'a Vec<i32>> = a; // Would be valid if `&mut T` covariant over `T`.
  *coerced_a = b;
}

let x = vec![1, 2, 3];
let mut y: &Vec<i32> = &x;
break_it(Box::new(y), &vec![]);
println!("{y:?}"); // Doesn't compile, `y` is no more.

I'd like to replace Allocator copy in allocator_api2::boxed::Box with Deallocator that would be ZST for bumpallo case.
But it requires existence of Deallocator and my proposal for it left unattended :)

@Boshen
Copy link
Member

Boshen commented Apr 12, 2024

@Boshen
Copy link
Member

Boshen commented Apr 12, 2024

Weird, this is causing a stack over only in the code coverage run https://github.com/oxc-project/oxc/actions/runs/8660479790/job/23749372302

For the record, this PR increased the stack size just over the edge.

A RUST_MIN_STACK=104857600 cargo codecov fixes the issue.

The underlying cause is oxc_prettier recursively printing assignment expressions.

/// This is used for over coming self-referential structs.
/// It is a memory leak if the boxed value has a `Drop` implementation.
pub struct Box<'alloc, T: ?Sized>(pub &'alloc mut T);
pub struct Box<'alloc, T: ?Sized>(NonNull<T>, PhantomData<&'alloc T>);

Choose a reason for hiding this comment

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

It should be PhantomData<(T, &'alloc ())> or something like this.
It should convey that Box owns T

Copy link
Member

Choose a reason for hiding this comment

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

Landing in #2952

Brooooooklyn referenced this pull request in toeverything/AFFiNE Apr 22, 2024
[![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [oxlint](https://oxc-project.github.io) ([source](https://togithub.com/oxc-project/oxc/tree/HEAD/npm/oxlint)) | [`0.2.17` -> `0.3.0`](https://renovatebot.com/diffs/npm/oxlint/0.2.17/0.3.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/oxlint/0.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/oxlint/0.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/oxlint/0.2.17/0.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/oxlint/0.2.17/0.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) |

---

### Release Notes

<details>
<summary>oxc-project/oxc (oxlint)</summary>

### [`v0.3.0`](https://togithub.com/oxc-project/oxc/releases/tag/oxlint_v0.3.0): oxlint v0.3.0

[Compare Source](https://togithub.com/oxc-project/oxc/compare/04f5fc018650d9a5dc6a4b1b40dea941fa07781e...b29aabd6f1a2e9e8cfa7db25c371ab40f79d02a5)

#### What's Changed

##### Potential breaking change

-   `-D all` no longer enables nursery rules, use `-D all -D nursery` instead

##### Features

-   support eslint globals by [@&#8203;Boshen](https://togithub.com/Boshen) in [https://github.com/oxc-project/oxc/pull/3038](https://togithub.com/oxc-project/oxc/pull/3038)
-   change no-empty-static-block to correctness
-   implement `--format checkstyle` by [@&#8203;Boshen](https://togithub.com/Boshen) in [https://github.com/oxc-project/oxc/pull/3044](https://togithub.com/oxc-project/oxc/pull/3044)
-   implement `--format unix` by [@&#8203;Boshen](https://togithub.com/Boshen) in [https://github.com/oxc-project/oxc/pull/3039](https://togithub.com/oxc-project/oxc/pull/3039)
-   implement fixer for `typescript-eslint/consistent-type-definitions` by [@&#8203;todor-a](https://togithub.com/todor-a) in [https://github.com/oxc-project/oxc/pull/3045](https://togithub.com/oxc-project/oxc/pull/3045)

##### Fixes

-   fix crashing with `unwrap` in import/no-cycle by [@&#8203;Boshen](https://togithub.com/Boshen) in [https://github.com/oxc-project/oxc/pull/3035](https://togithub.com/oxc-project/oxc/pull/3035)

**Full Changelog**: oxc-project/oxc@oxlint_v0.2.18...oxlint_v0.3.0

### [`v0.2.18`](https://togithub.com/oxc-project/oxc/releases/tag/oxlint_v0.2.18): oxlint v0.2.18

[Compare Source](https://togithub.com/oxc-project/oxc/compare/df11d10a2220e9aa7a33d9ab39ed662c2ba6fdb5...04f5fc018650d9a5dc6a4b1b40dea941fa07781e)

#### What's Changed

##### New Rules

-   correctness: eslint-plugin-unicorn no await in promise methods by [@&#8203;camc314](https://togithub.com/camc314) in [https://github.com/oxc-project/oxc/pull/2963](https://togithub.com/oxc-project/oxc/pull/2963)
-   correctness: eslint-plugin-unicorn no single promise in promise methods by [@&#8203;camc314](https://togithub.com/camc314) in [https://github.com/oxc-project/oxc/pull/2962](https://togithub.com/oxc-project/oxc/pull/2962)

##### Features

-   Add --jsdoc-plugin flag by [@&#8203;leaysgur](https://togithub.com/leaysgur) in [https://github.com/oxc-project/oxc/pull/2935](https://togithub.com/oxc-project/oxc/pull/2935)
-   Implement plugin-jsdoc/check-property-names by [@&#8203;leaysgur](https://togithub.com/leaysgur) in [https://github.com/oxc-project/oxc/pull/2989](https://togithub.com/oxc-project/oxc/pull/2989)
-   eslint/max-len by [@&#8203;woai3c](https://togithub.com/woai3c) in [https://github.com/oxc-project/oxc/pull/2874](https://togithub.com/oxc-project/oxc/pull/2874)
-   remove import/no-unresolved by [@&#8203;Boshen](https://togithub.com/Boshen) in [https://github.com/oxc-project/oxc/pull/3023](https://togithub.com/oxc-project/oxc/pull/3023)
-   support `oxlint-disable` alongside `eslint-disable` by [@&#8203;Boshen](https://togithub.com/Boshen) in [https://github.com/oxc-project/oxc/pull/3024](https://togithub.com/oxc-project/oxc/pull/3024)
-   jsdoc: Implement require-property rule by [@&#8203;leaysgur](https://togithub.com/leaysgur) in [https://github.com/oxc-project/oxc/pull/3011](https://togithub.com/oxc-project/oxc/pull/3011)
-   jsdoc: Implement require-property-(type|name|description) rules by [@&#8203;leaysgur](https://togithub.com/leaysgur) in [https://github.com/oxc-project/oxc/pull/3013](https://togithub.com/oxc-project/oxc/pull/3013)

#### New Contributors

-   [@&#8203;branchseer](https://togithub.com/branchseer) made their first contribution in [https://github.com/oxc-project/oxc/pull/2943](https://togithub.com/oxc-project/oxc/pull/2943)
-   [@&#8203;woai3c](https://togithub.com/woai3c) made their first contribution in [https://github.com/oxc-project/oxc/pull/2874](https://togithub.com/oxc-project/oxc/pull/2874)

**Full Changelog**: oxc-project/oxc@oxlint_v0.2.17...oxlint_v0.2.18

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/toeverything/AFFiNE).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy4zMDEuNCIsInVwZGF0ZWRJblZlciI6IjM3LjMxMy4xIiwidGFyZ2V0QnJhbmNoIjoiY2FuYXJ5IiwibGFiZWxzIjpbImRlcGVuZGVuY2llcyJdfQ==-->
@overlookmotel
Copy link
Member

overlookmotel commented Mar 5, 2025

@branchseer Sorry to resurrect a long-dead issue, but can you please help me with something?

What is it that makes allocator_api2::vec::Vec covariant, whereas bumpalo::collections::Vec isn't? Looking at the source code of both, the only difference I can see is that the alloc field of RawVec is &'a Bump in bumpalo, whereas it's a generic A: Allocator in allocator_api2.

But in our case, the Allocator we use with allocator_api2::vec::Vec is a &'a Bump anyway. So what's the difference?

https://docs.rs/allocator-api2/0.2.21/src/allocator_api2/stable/raw_vec.rs.html#116-120
https://docs.rs/bumpalo/latest/src/bumpalo/collections/raw_vec.rs.html#53-57

And weirdly, neither of them includes a PhantomData<T> which std::vec::Vec does.

Are you able to shed any light?

@branchseer
Copy link
Contributor Author

branchseer commented Mar 7, 2025

@overlookmotel

What is it that makes allocator_api2::vec::Vec covariant, whereas bumpalo::collections::Vec isn't?

In branchseer@8bebc89 I swapped allocator_api2::vec::Vec with bumpalo::collections::Vec. The covariant test assertion passes. So I was wrong and bumpalo::collections::Vec is indeed covariant. Sorry for the confusion:)

And weirdly, neither of them includes a PhantomData which std::vec::Vec does.

std::vet::Vec doesn't use T in its pointer type Unique<u8>, so PhantomData<T> is mandatory. allocator_api2 and bumpalo's Vec already uses T in their pointer type NonNull<T>.

@overlookmotel
Copy link
Member

overlookmotel commented Mar 10, 2025

In branchseer@8bebc89 I swapped allocator_api2::vec::Vec with bumpalo::collections::Vec. The covariant test assertion passes. So I was wrong and bumpalo::collections::Vec is indeed covariant. Sorry for the confusion:)

OK great. Thanks for investigating, and resolving the mystery. Maybe you were right at the time and bumpalo changed something since last year. 🤷

std::vet::Vec doesn't use T in its pointer type Unique<u8>, so PhantomData<T> is mandatory. allocator_api2 and bumpalo's Vec already uses T in their pointer type NonNull<T>.

Ah ha. Yes, of course. I did not spot the difference NonNull<T> vs Unique<u8>.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-ast Area - AST A-codegen Area - Code Generation A-linter Area - Linter A-minifier Area - Minifier A-parser Area - Parser A-semantic Area - Semantic

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants