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

Add lint against (some) interior mutable consts #132146

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

Urgau
Copy link
Member

@Urgau Urgau commented Oct 25, 2024

interior_mutable_consts

warn-by-default

The interior_mutable_consts lint detects instance where const-items have a interior mutable type, which silently does nothing.

Example

use std::sync::Once;

// SAFETY: should only be call once
unsafe extern "C" fn ffi_init() { /* ... */ }

const A: Once = Once::new(); // using `A` will always creates temporaries and
                             // never modify it-self on use, should be a
                             // static-item instead

fn init() {
    A.call_once(|| unsafe {
        ffi_init();  // unsound, as the `call_once` is on a temporary
                     // and not on a shared variable
    })
}
warning: interior mutability in `const` item have no effect to the `const` item it-self
  --> $DIR/interior-mutable-types-in-consts.rs:10:1
   |
LL | const A: Once = Once::new();
   | ^^^^^^^^^----^^^^^^^^^^^^^^^
   |          |
   |          `Once` is an interior mutable type
   |
   = note: each usage of a `const` item creates a new temporary
   = note: only the temporaries and never the original `const` item `A` will be modified
   = help: for use as an initializer, consider using an inline-const `const { /* ... */ }` at the usage site instead
   = note: `#[warn(interior_mutable_consts)]` on by default
help: for a shared instance of `A`, consider using a `static` item instead
   |
LL | static A: Once = Once::new();
   | ~~~~~~
help: alternatively consider allowing the lint
   |
LL | #[allow(interior_mutable_consts)] const A: Once = Once::new();
   | +++++++++++++++++++++++++++++++++

Explanation

Using a const-item with an interior mutable type has no effect as const-item are essentially inlined wherever they are used, meaning that they are copied directly into the relevant context when used rendering modification through interior mutability ineffective across usage of that const-item.

The current implementation of this lint only warns on significant std and core interior mutable types, like Once, AtomicI32, ... this is done out of prudence and may be extended in the future.


It should be noted that the lint in not immunized against false-positives in particular when those immutable const-items are used as "init" const for const arrays (i.e. [INIT; 10]), which is why the lint recommends inline-const instead.

It should also be noted that this is NOT an uplift of the more general clippy::declare_interior_mutable_const lint, which works for all interior mutable types, but has even more false-positives than the currently proposed lint.

A simple Github Search reveals many instance where the user probably wanted to use a static-item instead.


@rustbot labels +I-lang-nominated +T-lang -S-waiting-on-review
cc @AngelicosPhosphoros @kupiakos
r? compiler

Fixes IRLO - Forbidding creation of constant mutexes, etc
Fixes #132028
Fixes #40543

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue. I-lang-nominated Nominated for discussion during a lang team meeting. T-lang Relevant to the language team, which will review and decide on the PR/issue. labels Oct 25, 2024
@taiki-e
Copy link
Member

taiki-e commented Oct 25, 2024

This lint seems to be the same as (or limited version of) clippy::declare_interior_mutable_const?

AFAIK clippy::declare_interior_mutable_const is usually useless (It is not a lint that is triggered for code that may actually encounter problems, e.g., it is not triggered when a constant defined in the external crate is used incorrectly. clippy::borrow_interior_mutable_const covers actual footguns about interior_mutable_const and triggered for code that may actually encounter problems. see also rust-lang/rust-clippy#7665)

@rust-log-analyzer

This comment has been minimized.

@Urgau
Copy link
Member Author

Urgau commented Oct 25, 2024

This lint seems to be the same as (or limited version of) clippy::declare_interior_mutable_const?

Forgot to mentioned it (fixed), but yes it's a very-limited form of the clippy lint.

AFAIK clippy::declare_interior_mutable_const is usually useless (It is not a lint that is triggered for code that may actually encounter problems, e.g., it is not triggered when a constant defined in the external crate is used incorrectly.

I disagree that it is useless. It detects suspicious constants item, suspicious enough that they are probably a mistake and IMO warrants a warning.

In other words I think this lint detects potential issues at the root cause and not at the usage site. I would find it weird if we warned when using a const-item from an external crate, where the user can't do anything about it (except maybe creating an issue upstream) but I also think I could be convinced otherwise.

clippy::borrow_interior_mutable_const covers actual footguns about interior_mutable_const and triggered for code that may actually encounter problems.

I did not actually see this lint before, it's interesting and tackles the issue at another level; but IMO declaring a interior mutable const is a foot-gun whenever used or not, in particular if exposed and used from an external crate.

see also rust-lang/rust-clippy#7665)

This should use inline const, which is suggested by this lint.

@rustbot
Copy link
Collaborator

rustbot commented Oct 25, 2024

Some changes occurred in src/tools/clippy

cc @rust-lang/clippy

@rust-log-analyzer

This comment has been minimized.

@taiki-e
Copy link
Member

taiki-e commented Oct 25, 2024

@Urgau

In other words I think this lint detects potential issues at the root cause and not at the usage site. I would find it weird if we warned when using a const-item from an external crate, where the user can't do anything about it (except maybe creating an issue upstream).

Ah, we each seem to have a different case in mind. I was thinking of a case where const is correct and the usage referring to it is incorrect, and you seem to be thinking of a case where defining it as const is wrong and the usage referring to it is supposedly correct. I agree that this lint is indeed not necessarily useless in such a case.

This should use inline const, which is suggested by this lint.

It seems unfortunate that lint, for which the only two ways to deal with warnings are to ignore the warning or increase the MSRV, will be added as warn-by-default.


After reading your comment, I think it would be better to apply this lint only to public constants, and uplift clippy::borrow_interior_mutable_const to rustc to address other cases:

  • In cases where const is not correct (i.e., static is correct):
    • if const is private:
      • borrow_interior_mutable_const warns of a reference to const and the user can correct it on the fly.
      • interior_mutable_consts(declare_interior_mutable_const) doesn't need to do anything since borrow_interior_mutable_const can catch all problems.
    • if const is public:
      • borrow_interior_mutable_const warns of a reference to const. If it is defined in the external crate, the user cannot fix the underlying problem, but at least the problem can be avoided.
      • interior_mutable_consts(declare_interior_mutable_const) warns of a suspect definition of const in the crate that defines that const.
  • In cases where const is correct:
    • if const is private:
      • borrow_interior_mutable_const warns misuses of it.
      • interior_mutable_consts(declare_interior_mutable_const) doesn't need to do anything since borrow_interior_mutable_const can catch all misuses.
    • if const is public:
      • borrow_interior_mutable_const warns misuses of it.
      • interior_mutable_consts(declare_interior_mutable_const) warns of (correct) definition of const (false positive!).
        • However, in cases where const is correct, such as array initialization helpers, const is basically private, so the chances of encountering this false positive should be quite rare...

@rustbot
Copy link
Collaborator

rustbot commented Oct 25, 2024

Some changes occurred in compiler/rustc_codegen_cranelift

cc @bjorn3

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@bors

This comment was marked as resolved.

@Urgau
Copy link
Member Author

Urgau commented Oct 26, 2024

[..] you seem to be thinking of a case where defining it as const is wrong and the usage referring to it is supposedly correct.

Yes, there seems to be some of cases in the wildly were it's the case. (both of the linked issues are example of that)

Example of a soundness issue caused by using a const-item instead of a static-item
use std::sync::Once;

// SAFETY: should only be call once
unsafe extern "C" fn ffi_init() { /* ... */ }

const INIT_ONCE: Once = Once::new(); // using `B` will always creates temporaries and
                                     // never modify it-self on use, should be a
                                     // static-item instead

fn init() {
    INIT_ONCE.call_once(|| unsafe {
        ffi_init();  // unsound, as the `call_once` is on a temporary
                     // and not on a shared variable
    })
}

Updated the lint example with this one, to better describe the usefulness of the lint

After reading your comment, I think it would be better to apply this lint only to public constants, and uplift clippy::borrow_interior_mutable_const to rustc to address other cases

Interesting idea.

I'm not sure I like having the lint be dependant of the visibility of the const-item since it has nothing to do with the issue it-self, see my example above or #132028, where the issue is not the usage but the declaration, with your proposal we IMO would be linting at the wrong place. (We could lint at the declaration only if we found a usage, but that seems hacky and I rather avoid that)

@rust-log-analyzer

This comment has been minimized.

@@ -1,5 +1,5 @@
#![warn(clippy::declare_interior_mutable_const)]

#![allow(interior_mutable_consts)]
Copy link
Member

Choose a reason for hiding this comment

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

How do those Clippy lints interact with the new rustc lint?

Copy link
Member Author

Choose a reason for hiding this comment

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

This proposed lint is similar to clippy::declare_interior_mutable_const, except that contrary to the Clippy lint which recurse into the type and can lint on arbitrary type that have interior mutability, this lint only checks if the type is marked with #[rustc_significant_interior_mutable_type] (which only concerns a few core/std types).

So for these types (and only these types) we would report a similar warning. (Preventing this duplicated warning should be a matter of checking if the type as the attribute).

Copy link
Member

Choose a reason for hiding this comment

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

I would prefer adding this check to the Clippy lints, over just allowing the rustc lint in the tests.

The tests could then be adapted to remove all test cases that get linted by rustc, except for maybe 1 to check that the Clippy lint doesn't actually trigger on those core/std types.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
I-lang-nominated Nominated for discussion during a lang team meeting. S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-lang Relevant to the language team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue.
Projects
None yet
8 participants