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

#[linkage = "weak"] const fn accepted without warning #134451

Open
HaoboGu opened this issue Dec 18, 2024 · 14 comments
Open

#[linkage = "weak"] const fn accepted without warning #134451

HaoboGu opened this issue Dec 18, 2024 · 14 comments
Labels
A-diagnostics Area: Messages for errors, warnings, and lints A-linkage Area: linking into static, shared libraries and binaries C-discussion Category: Discussion or questions that doesn't represent real issues. F-linkage #![feature(linkage)]` T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@HaoboGu
Copy link

HaoboGu commented Dec 18, 2024

I'm trying to define a "weak" const in a library, which allows users to redefine their const. On stable Rust, there's no way to do it. On nightly, linkage feature seems the solution, but I found that it doesn't work as well. The following is the repro code:

// In library code:
#![feature(linkage)]

#[linkage = "weak"]
#[no_mangle]
const fn get_foo() -> i32 {
    0
}

pub const FOO: i32 = get_foo();

pub fn hello() {
    println!("hello: {}, {}", FOO, get_foo());
}

// In application code:
use linkage_weak_bug::hello;

#[no_mangle]
const fn get_foo() -> i32 {
    1
}

fn main() {
    // Output: hello: 0, 1
    // Expected: hello: 1, 1
    hello();
}

I uploaded a repo that could repro it: https://github.com/HaoboGu/linkage_weak_bug

@HaoboGu HaoboGu added the C-bug Category: This is a bug. label Dec 18, 2024
@rustbot rustbot added the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Dec 18, 2024
@zopsicle
Copy link
Contributor

zopsicle commented Dec 18, 2024

Const items are always evaluated at compile time, which happens before linking, so this is expected. Perhaps const functions should not be allowed weak linkage, because it allows their compile-time and runtime behavior to differ.

@jieyouxu jieyouxu added T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. F-linkage #![feature(linkage)]` labels Dec 18, 2024
@HaoboGu
Copy link
Author

HaoboGu commented Dec 18, 2024

I think it's valuable to have "weak constants" in Rust, just disallowing weak linkage for const fns is not we want.

For example, in no_std env, it's very common to define a global channel with a fixed size:

// This is a simplified code
const CH_SIZE: usize = 4;
static CHANNEL: Channel<CH_SIZE> = Channel::new();

And, as a library author, we may want the CH_SIZE to be customizable by library users. It's impossible for Rust without using weak linkage now.

@fmease fmease changed the title [linkage = "weak"] doesn't work for const fn #[linkage = "weak"] doesn't work for const fn Dec 18, 2024
@fmease fmease added the A-linkage Area: linking into static, shared libraries and binaries label Dec 18, 2024
@bjorn3
Copy link
Member

bjorn3 commented Dec 18, 2024

CHANNEL gets fully codegened when compiling the crate that contains it. To do this, it's full type needs to be known, which in turn requires CH_SIZE to be known. Note that the very fact if compilation succeeds or not and which functions get codegened can depend on the value of a const item or const function. As such weak linkage for const items and const functions can't work. And that is not even taking into account the fact that const items by definition don't have a symbol name unlike statics, but weak linkage matches on symbol name.

@bjorn3 bjorn3 added C-discussion Category: Discussion or questions that doesn't represent real issues. and removed C-bug Category: This is a bug. labels Dec 18, 2024
@workingjubilee
Copy link
Member

Indeed, we may wish to investigate making this a compiler error.

But first, while an expert on the linking side of things has spoken, let us consult the experts on the CTFE side of things. cc @rust-lang/wg-const-eval do any of you wish to entertain the possibility of a const fn having its symbol name overwritten at runtime, and users like @HaoboGu consequently seriously misunderstanding the model?

@workingjubilee
Copy link
Member

@HaoboGu The idea of "generic singletons" is an interesting idea, but it's absolutely not the way things work, and if it did work, it would have to be designed in such a way as to actually be evaluated without taking linkage into account.

@RalfJung
Copy link
Member

RalfJung commented Dec 18, 2024

No I don't think const fn intends to ever support terrible linker hacks like that. ;) If there's a use-case here, it should be solved with proper, type-safe Rust mechanisms, not at the linker level. What should even happen if 3 crates each overwrite get_foo (or CH_SIZE) with a different value? I don't see how we could have a sensible safe system here.

Even on regular fn this is rather questionable, considering things like inlining. What even is the semantics here? When you call get_foo it non-deterministically calls some function with that name? AFAIK currently we have a safety requirement on no_mangle that there must be only one function with that name, so technically your code has UB. We probably want to adjust this to support weak linkage since people want to use their terrible linker hacks, but we should do so carefully.

@RalfJung
Copy link
Member

RalfJung commented Dec 18, 2024

And, as a library author, we may want the CH_SIZE to be customizable by library users.

Constants are called that because they must be constant. It would be unsound to let anyone change them. The type system relies hard to the fact that the same constant has the same value everywhere in the crate graph.

If you want to write your code in a way that is generic over some constant, you need to make it explicitly generic. We have mechanisms for that already (associated consts in a trait). I know that making an entire module generic can be annoying, but the solution for that definitely does not involve fundamentally changing the nature of consts in Rust; the solution is to explore module-level generics.

@workingjubilee
Copy link
Member

Getting slightly further afield, but "weak" isn't even a coherent runtime concept on all OS, and if memory serves the way it is handled for MachO and ELF can be slightly, subtly different.

@HaoboGu
Copy link
Author

HaoboGu commented Dec 18, 2024

Thanks for everyone's explanation! Now I know doing it when linking is not proper :)

If there's a use-case here, it should be solved with proper, type-safe Rust mechanisms

I agree! Currently it's very difficult to achieve this, especially in embedded world. There are many ways to "bypass" it, for example, embassy generates lots of feature gates, let users choose the tich-hz via feature gate, and toml-cfg does a hacky way via proc-macro, analyzing the out dir of all dependencies.

We have mechanisms for that already (associated consts in a trait).

It's a little bit weird if we convert a config struct to a trait, and tell users to fill an associated const if they want to customize the config. But yeah, now I see this should work. Thanks for the pointer!

@RalfJung
Copy link
Member

The idea of "generic singletons" is an interesting idea, but it's absolutely not the way things work, and if it did work, it would have to be designed in such a way as to actually be evaluated without taking linkage into account.

Speaking of singletons, this reminds me of rust-lang/rfcs#3632 / rust-lang/rfcs#3635. So maybe there is a space for "externally defineable consts" in a similar vein?

@HaoboGu
Copy link
Author

HaoboGu commented Dec 18, 2024

Speaking of singletons, this reminds me of rust-lang/rfcs#3632 / rust-lang/rfcs#3635. So maybe there is a space for "externally defineable consts" in a similar vein?

yes, seems very reasonable:)

@workingjubilee
Copy link
Member

Plausible. cc @m-ou-se if you have any thoughts (or not if not, it's just a data point of possible interest).

@jieyouxu jieyouxu removed the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Dec 19, 2024
@m-ou-se
Copy link
Member

m-ou-se commented Dec 19, 2024

Yeah that's definitely on our list. We're now calling our project "externally implementable items", and consts are also on our list of items, although not our first priority. We'll see how far we can get. :)

@workingjubilee workingjubilee added the A-diagnostics Area: Messages for errors, warnings, and lints label Dec 21, 2024
@workingjubilee
Copy link
Member

Cool!

This issue is now about diagnostics! We should at least warn, probably compile error on this.

@workingjubilee workingjubilee changed the title #[linkage = "weak"] doesn't work for const fn #[linkage = "weak"] const fn accepted without warning Dec 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-diagnostics Area: Messages for errors, warnings, and lints A-linkage Area: linking into static, shared libraries and binaries C-discussion Category: Discussion or questions that doesn't represent real issues. F-linkage #![feature(linkage)]` T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

9 participants