Skip to content

Lint check for using incorrect types in native fns should check crust functions #1843

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

Closed
brson opened this issue Feb 14, 2012 · 6 comments
Closed
Labels
A-FFI Area: Foreign function interface (FFI) A-lints Area: Lints (warnings about flaws in source code) such as unused_mut. P-low Low priority

Comments

@brson
Copy link
Contributor

brson commented Feb 14, 2012

No description provided.

@ghost ghost assigned brson Apr 12, 2012
@pnkfelix
Copy link
Member

@brson what is this bug asking for, more specifically?

  • Which particular lint check is it referring to? I've looked through librustc/midde/lint.rs and cannot find anything that seems to match this description. (I also checked out the code circa one year ago and did not see anything obvious.)
  • I know that the syntax for defined a so-called "crust" function has changed significantly in the past year. It looks like the appropriate way to do it at the moment is: extern "Rust" fn. (The crust use-case needs to be documented, at least in the FFI tutorial.)
  • Most odd of all, it seems like we have a uniform syntax for associating calling conventions with particular function pointers, but we do not seem to properly support using it. (I elaborate more on this below.)

It also looks like and that there are some hidden gotcha's that need to be addressed, like the following:

(In these examples, I am linking to a simple object file whose C source, ntwice.c, is below as well.)

The first example compiles, but something is wrong with the calling convention, and it produces the wrong answer:

type c_int = ::std::libc::c_int;

mod ntwice {
    type c_int = ::std::libc::c_int;

    #[abi = "cdecl"]
    pub extern {
        fn twice(tf: *u8, x: c_int) -> c_int;
        fn callback(rf: *u8, // <===== no decl for callback
                    x: c_int) -> c_int;
    }
}

extern fn incr2(x:c_int) -> c_int { x+2 }

extern "Rust" // <===== extern decl; none given for `callback` param `rf`.
fn rtwice(f: extern "C" fn(c_int) -> c_int, x: c_int) -> c_int {
    println(fmt!("---- Rust rtwice calling f(%?)", x));
    let ret = f(f(x));
    println(fmt!("---- Rust rtwice calling f(%?) => %?", x, ret));
    ret
}

fn main() {
    println("Hello world");
    unsafe {
        let x = ntwice::twice(incr2, 3);
        println(fmt!("Rust ntwice::twice(incr2, 3): %?", x));
        let y = ntwice::callback(rtwice, 4);
        println(fmt!("Rust ntwice::callback(rtwice, 4): %?", y));
    }
}
+ rustc --link-args ntwice.o play-foreign.rs
+ ./play-foreign
Hello world
-- C twice calling f(3)
-- C twice done w/ f(3) => 7
Rust ntwice::twice(incr2, 3): 7
-- C callback calling rtwice(incr3, 4)
---- Rust rtwice calling f(4)
------ C Enter incr3(f, 120493568)
------ C Finis incr3(f, 120493568) => 120493571
------ C Enter incr3(f, 1283)
------ C Finis incr3(f, 1283) => 1286
---- Rust rtwice calling f(4) => 1286
-- C callback done w/ rtwice(incr3, 4) => 1286
Rust ntwice::callback(rtwice, 4): 1286

This second example also compiles, but something again goes wrong with the calling convention, and it segfaults on my machine:

type c_int = ::std::libc::c_int;

mod ntwice {
    type c_int = ::std::libc::c_int;

    #[abi = "cdecl"]
    pub extern {
        fn twice(tf: *u8, x: c_int) -> c_int;
        // a "real" decl for callback
        fn callback(rf: extern "Rust" fn(extern "C" fn(c_int) -> c_int,
                                         x: c_int) -> c_int,
                    x: c_int) -> c_int;
    }
}

extern fn incr2(x:c_int) -> c_int { x+2 }

// <===== no extern decl, unlike `callback` param `rf`.
fn rtwice(f: extern "C" fn(c_int) -> c_int, x: c_int) -> c_int {
    println(fmt!("---- Rust rtwice calling f(%?)", x));
    let ret = f(f(x));
    println(fmt!("---- Rust rtwice calling f(%?) => %?", x, ret));
    ret
}

fn main() {
    println("Hello world");
    unsafe {
        let x = ntwice::twice(incr2, 3);
        println(fmt!("Rust ntwice::twice(incr2, 3): %?", x));
        let y = ntwice::callback(rtwice, 4);
        println(fmt!("Rust ntwice::callback(rtwice, 4): %?", y));
    }
}
+ rustc --link-args ntwice.o play-foreign2.rs
+ ./play-foreign2
Hello world
-- C twice calling f(3)
-- C twice done w/ f(3) => 7
Rust ntwice::twice(incr2, 3): 7
-- C callback calling rtwice(incr3, 4)
---- Rust rtwice calling f(2104208)
./test-play-foreign.sh: line 14: 47675 Segmentation fault: 11  ./play-foreign2

This third example fails to compile, I assume because a fn item prefixed by extern "Rust" is treated as having the type *u8; it would be better if we could have it carry around the more elaborate type that is reported by rustc that is assigned to the parameter f

type c_int = ::std::libc::c_int;

mod ntwice {
    type c_int = ::std::libc::c_int;

    #[abi = "cdecl"]
    pub extern {
        fn twice(tf: *u8, x: c_int) -> c_int;
        // a "real" decl for callback
        fn callback(rf: extern "Rust" fn(extern "C" fn(c_int) -> c_int,
                                         x: c_int) -> c_int,
                    x: c_int) -> c_int;
    }
}

extern fn incr2(x:c_int) -> c_int { x+2 }

extern "Rust" // <===== extern decl matches that of `callback` param `rf`.
fn rtwice(f: extern "C" fn(c_int) -> c_int, x: c_int) -> c_int {
    println(fmt!("---- Rust rtwice calling f(%?)", x));
    let ret = f(f(x));
    println(fmt!("---- Rust rtwice calling f(%?) => %?", x, ret));
    ret
}

fn main() {
    println("Hello world");
    unsafe {
        let x = ntwice::twice(incr2, 3);
        println(fmt!("Rust ntwice::twice(incr2, 3): %?", x));
        let y = ntwice::callback(rtwice, 4);
        println(fmt!("Rust ntwice::callback(rtwice, 4): %?", y));
    }
}
+ rustc --link-args ntwice.o play-foreign3.rs
play-foreign3.rs:30:33: 30:39 error: mismatched types: expected `extern "Rust" fn(extern "C" fn(i32) -> i32, i32) -> i32` but found `*u8` (expected extern fn but found *-ptr)
play-foreign3.rs:30         let y = ntwice::callback(rtwice, 4);
                                                     ^~~~~~
error: aborting due to previous error

ntwice.c:

#include <stdio.h>

int twice(int (*f)(int), int x) {
    int ret, arg;
    printf("-- C twice calling f(%d)\n", x);
    ret = f(f(x));
    printf("-- C twice done w/ f(%d) => %d\n", x, ret);
    return ret;
}

int incr3(int x) {
    int ret;
    printf("------ C Enter incr3(f, %d)\n", x);
    ret = x + 3;
    printf("------ C Finis incr3(f, %d) => %d\n", x, ret);
    return ret;
}

int callback(int (*rtwice)(int (*f)(int), int x), int x) {
    int ret;
    printf("-- C callback calling rtwice(incr3, %d)\n", x);
    ret = rtwice(incr3, x);
    printf("-- C callback done w/ rtwice(incr3, %d) => %d\n", x, ret);
    return ret;
}

@pnkfelix
Copy link
Member

brson did write a recent mailing list post answering some FFI questions and noting current hacks versus long term plans, see: https://mail.mozilla.org/pipermail/rust-dev/2013-May/004107.html

@pnkfelix
Copy link
Member

visiting for bug triage, email 2013-08-19.

I don't think the questions I posted to brson have been answered. To be fair, I haven't taken the time to try to distill my questions down to a single three line example, so its okay that they haven't been answered.

But nonetheless, I don't know how to categorize this ticket without more information.

@nikomatsakis
Copy link
Contributor

A lot of this has changed with my recent pushes, but I think what @brson was referring to specifically was the lint that checks that people do not use int or uint in extern fn declarations. The purpose of this lint is because int in C is (typically) always 32 bits, whereas in Rust it varies depending on the target architecture. So we encourage people to write either c_int or uintptr_t, depending on what they wanted.

Many of your other questions, specifically regarding the ABI of extern fns and the like, aren't relevant to this bug per se, but I think that they ought to work now.

@catamorphism
Copy link
Contributor

Low priority, no milestone

@steveklabnik
Copy link
Member

The purpose of this lint is because int in C is (typically) always 32 bits, whereas in Rust it varies depending on the target architecture.

oh noooooooooo 😉

I think this ticket is old enough to be completely outdated, especially with the changes to int. If I'm wrong, please re-open.

celinval pushed a commit to celinval/rust-dev that referenced this issue Jun 4, 2024
Kobzol pushed a commit to Kobzol/rust that referenced this issue Dec 30, 2024
bors pushed a commit to rust-lang-ci/rust that referenced this issue Jan 2, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-FFI Area: Foreign function interface (FFI) A-lints Area: Lints (warnings about flaws in source code) such as unused_mut. P-low Low priority
Projects
None yet
Development

No branches or pull requests

6 participants