Skip to content

New lints: #[target_feature] changes #1246

@obi1kenobi

Description

@obi1kenobi

As of Rust 1.86, #[target_feature] can be applied to safe functions, so we should look at what it means in terms of breaking changes.

Reading the reference page for this attribute is highly recommended before proceeding with this issue.

Adding #[target_feature] to a safe pub function that didn't have it before is a major breaking change, since the function no longer implements its corresponding Fn() traitplayground

Adding #[target_feature] to a unsafe pub function that didn't have it before is usually a major breaking change, since the safety obligations for that function have changed. The exception is if the newly-requested feature was already enabled on that target, or it's implied by another already-requested feature. For example: x86-64 with any OS at all enables sse and sse2 by default, and avx2 implies avx. Information on whether required features are explicit or implied, or globally enabled, is available in the schema.

If a safe pub function already had #[target_feature], adding a new feature is always a major breaking change. This lint shouldn't trigger if the function became unsafe as well — that breakage dominates.

If an unsafe pub function already had #[target_feature], adding a new feature is only major breaking if the feature isn't implied nor already globally enabled, as above. This lint should trigger even if the function might have become safe in the meantime.

If an unsealed trait's associated function has a feature removed from its existing #[target_feature] list, that's a major breaking change since Rust currently allows impls of that trait to specify a stricter (i.e. the previous) #[target_feature] without any warning. Such downstream impls may suddenly be hit with UB when a caller that uses impl Trait or dyn Trait with their impl (with stricter target feature requirements) is used based on the trait's looser declared target feature requirements. The exception to the breakage is when the removed feature continues to be implied by either the platform (see above x86-64 example) or by another feature that remains declared.

Related false-positives

#[target_feature] also appears to trigger a bug in rustdoc JSON that causes false-positives in two of our lints: function_unsafe_added and inherent_method_unsafe_added. The cause is this: rust-lang/rust#142655

Open questions

  • How do we find out which features are effectively implied by which target? This seems to be one of those things that "everyone knows" but isn't really written down directly in that explicit form. (answered below)
  • How do we figure out which target's "implied" rules we should be looking at in cargo-semver-checks? We may have to ask rustdoc to describe the target while generating rustdoc JSON.
  • How do we encode the "implies" rules? Do we put them in the schema (as "adapter magic"), or do we make some wild pile of regexes, or something else? (answered below)
  • With regard to the above, how will we handle cases when new features are added to the list? We may only support target features that are considered stable (i.e. "unstable features don't auto-enable any other features and are never automatically enabled themselves"), and rely on stabilization announcements to be notified of changes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-lintArea: new or existing lintE-help-wantedCall for participation: Help is requested to fix this issue.E-mentorCall for participation: Mentorship is available for this issue.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions