toku-sa-n
toku-sa-n

Reputation: 894

How to avoid `#[global_allocator]` conflicts?

Suppose I have three crates: foo, bar, and baz.

Cargo.toml:

[workspace]
members = [
    "foo",
    "bar",
    "baz",
]

[profile.dev]
panic = "abort"

[profile.release]
panic = "abort"

foo/Cargo.toml:

[package]
name = "foo"
version = "0.1.0"
edition = "2018"

[lib]
name = "foo"
crate-type = ["staticlib"]

[dependencies]
bar = { path = "../bar", default-features = false }

foo/src/lib.rs:

#![no_std]

extern crate bar as _;

use core::alloc::{GlobalAlloc, Layout};

#[global_allocator]
static ALLOCATOR: Allocator = Allocator;

struct Allocator;
unsafe impl GlobalAlloc for Allocator {
    unsafe fn alloc(&self, _: Layout) -> *mut u8 {
        todo!()
    }

    unsafe fn dealloc(&self, _: *mut u8, _: Layout) {
        todo!()
    }
}

bar/Cargo.toml:

[package]
name = "bar"
version = "0.1.0"
edition = "2018"

[lib]
name = "bar"

[features]
default = ["heap"]
heap = []

bar/src/lib.rs:

#![no_std]
#![feature(alloc_error_handler)]

use core::alloc::GlobalAlloc;
use core::alloc::Layout;

extern crate alloc;

#[cfg(feature = "heap")]
#[global_allocator]
static ALLOCATOR: Allocator = Allocator;

struct Allocator;
unsafe impl GlobalAlloc for Allocator {
    unsafe fn alloc(&self, _: Layout) -> *mut u8 {
        todo!()
    }

    unsafe fn dealloc(&self, _: *mut u8, _: Layout) {
        todo!()
    }
}

#[panic_handler]
fn panic(_: &core::panic::PanicInfo<'_>) -> ! {
    todo!();
}

#[alloc_error_handler]
fn alloc_fail(_: Layout) -> ! {
    todo!();
}

baz/Cargo.toml:

[package]
name = "baz"
version = "0.1.0"
edition = "2018"

[lib]
name = "baz"
crate-type = ["staticlib"]

[dependencies]
bar = { path = "../bar" }

baz/src/lib.rs:

#![no_std]

extern crate bar as _;

Here, bar provides the global allocator, and both foo and baz depend on bar. However, foo doesn't enable bar's global allocator to use its original allocator, so foo depends on bar without the default features.

Running cargo clippy on each directory (foo/, bar/, and baz/) succeeds. However, running the command on the project root causes an error:

%cargo clippy
    Checking bar v0.1.0 (/tmp/tmp.wlBjM4wNFx/bar)
    Checking baz v0.1.0 (/tmp/tmp.wlBjM4wNFx/baz)
    Checking foo v0.1.0 (/tmp/tmp.wlBjM4wNFx/foo)
error: the `#[global_allocator]` in this crate conflicts with global allocator in: bar

error: could not compile `foo` due to previous error

Actually, there are no global allocator conflicts as foo uses its original global allocator and disables bar's one. How to avoid this error?

Versions

rustup 1.24.3 (ce5817a94 2021-05-31)
cargo 1.56.0-nightly (b51439fd8 2021-08-09)
rustc 1.56.0-nightly (0035d9dce 2021-08-16)

Upvotes: 5

Views: 2304

Answers (1)

toku-sa-n
toku-sa-n

Reputation: 894

This is workspace's property. bjorn3 said on Zulip:

If foo, bar and baz are all compiled together, the heap feature will be globally enabled for bar if either foo or baz depends on it. Cargo will take the union of all the features each dependent crate requires and compile dependencies once with these features.

So, bar is compiled with the heap feature because baz requires it, its feature is also enabled when foo is compiled. Thus, both foo's and bar's global allocators are enabled simultaneously, causing the error.

Upvotes: 3

Related Questions