Skip to content

Multiple incompatible clashing extern fn's should be a hard error (potentially phased in via future-incompat) #105146

Open
@pnkfelix

Description

@pnkfelix

(Many thanks to @celinval for alerting me to the existence of this issue.)

I tried this code (playpen):

pub mod a {
    use std::ffi::c_int;
    extern "C" { fn isalpha(_: c_int) -> c_int; }
    pub fn wrap(x: c_int) -> c_int { unsafe { isalpha(x) } }
}

pub mod b {
    use std::ffi::c_int;
    extern "C" { fn isalpha(_: c_int, _: c_int) -> c_int; }
    pub fn wrap(x: c_int, y: c_int) -> c_int { unsafe { isalpha(x, y) } }
}

fn main() {
    dbg!(a::wrap(10));
    dbg!(b::wrap(10, 20));
}

I expected to see this happen: code should be rejected as obviously wrong, since within the same codegen unit, it is using two incompatible declarations for the same external foreign function.

Instead, this happened: the code is accepted by the compiler with just a warning:

warning: `isalpha` redeclared with a different signature
 --> src/main.rs:9:18
  |
3 |     extern "C" { fn isalpha(_: c_int) -> c_int; }
  |                  ------------------------------ `isalpha` previously declared here
...
9 |     extern "C" { fn isalpha(_: c_int, _: c_int) -> c_int; }
  |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
  |
  = note: `#[warn(clashing_extern_declarations)]` on by default
  = note: expected `unsafe extern "C" fn(i32) -> i32`
             found `unsafe extern "C" fn(i32, i32) -> i32`

This lint was added in PR #70946; there was an attempt, in PR #75192, to make the lint deny-by-default, but that attempt was abandoned (see associated comment explaining why that was abandoned).

Its important to note in particular that in the case of these clashing declarations, you can see in the generated LLVM-IR that there is truly only one declaration chosen, and the order in which one feeds the mod a and mod b above will dictate which extern fn declaration is selected by LLVM.

(Its possible that we may need a more precise variant of the existing clashing_extern_declarations lint that more precisely identifies the problematic cases.)

I personally suspect that this error represents enough of a red flag that we really should be aiming to turn the existing lint into a hard-error, even if only over an edition boundary for the language.

Meta

rustc --version --verbose:

Stable channel

Build using the Stable version: 1.65.0

Nightly channel

Build using the Nightly version: 1.67.0-nightly (2022-11-30 c97b539)

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-FFIArea: Foreign function interface (FFI)A-lintsArea: Lints (warnings about flaws in source code) such as unused_mut.C-bugCategory: This is a bug.C-future-incompatibilityCategory: Future-incompatibility lintsT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    Status

    Idea

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions