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

Allow generic foreign functions. #15831

Merged
merged 2 commits into from
Aug 7, 2014
Merged

Allow generic foreign functions. #15831

merged 2 commits into from
Aug 7, 2014

Conversation

rpjohnst
Copy link
Contributor

This allows for things like this:

extern "C" fn callback<T>(t: T) { /* ... */ }
extern "C" {
    fn take_callback(c: extern fn(i32));
}

and later:

take_callback(callback::<i32>);

Closes #12502.

@alexcrichton
Copy link
Member

cc #10353, in particular this comment:

Current thinking is to put in a proper error message now (as in, don't add support in the short term for 1.0).

Then post 1.0 we can backward-compatibly extend rustc to add support for this.

That being said, I believe times have changed such that an extern fn is now a first-class type instead of having the type *u8 (in the very olden days), and I've certainly found this to be useful from time to time.

One common use of extern fn is to attach the #[no_mangle] attribute to it, however, and I believe that this implementation may lead to surprising results. Could you add a test to make sure nothing too surprising happens?

y: T
}

pub extern "C" fn foo<T>(ts: TestStruct<T>) -> T { s.y }
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't seem to be used from test.rs, could you be sure to use it and the struct?

@huonw
Copy link
Member

huonw commented Jul 20, 2014

Maybe #[no_mangle] should just be illegal on a generic function? (What do we do now for non extern generic functions?)

@rpjohnst
Copy link
Contributor Author

Currently, #[no_mangle] is ignored on generic functions. With this patch, that behavior is preserved so #[no_mangle] works on non-generic externs and is ignored on generic externs. It would be nice to have an error or warning in the cases it's currently ignored, but that's probably a separate issue.

I've pushed a new commit that (hopefully) addresses @alexcrichton's comments above, adding a test that non-generic functions (extern and non) can be successfully #[no_mangle]d, and using testcrate::foo in test.rs (it looks like it already was, but without the testcrate:: ...?)

@sfackler
Copy link
Member

If it's ignored "properly", it'll at least trigger the unused attribute warning.

@rpjohnst
Copy link
Contributor Author

It's ignored improperly in both cases right now. I've filed bug #15844.

@alexcrichton
Copy link
Member

Could you add a test to ensure that this works as expected?

#[no_mangle]
extern fn foo<T: Int>(a: T, b: T) -> T { a + b }

fn main() {
    assert_eq!(99u8, foo(255u8, 100u8));
    assert_eq!(99u16, foo(65535u16, 100u16));
}

In an ideal world it would yield a warning that the #[no_mangle] attribute is unused, but I want to make sure that we don't ICE or cause linker errors in today's world. It's ok if it doesn't yield a warning just yet.

@sfackler
Copy link
Member

I'll look into the lack of a warning.

@lilyball
Copy link
Contributor

Travis failure looks legit. 14 run-pass tests either crashed the compiler or, interestingly, failed to invoke the linker due to memory exhaustion.

@alexcrichton
Copy link
Member

That actually happens pretty frequently, it's safe to ignore. The travis builders don't necessarily have a lot of umph behind them.

@lilyball
Copy link
Contributor

@alexcrichton If it was just memory, sure. But a bunch of tests actually crashed with SIGILL, which AFAIK is not a known expected failure case of Travis.

@sfackler
Copy link
Member

An OOM will result in a SIGILL since it calls the abort intrinsic.

@lilyball
Copy link
Contributor

Without printing anything to stdout? Oh. That's kind of surprising. Also, I was under the impression that allocation failure resulted in task failure, but I don't know where I got that idea from.

@thestinger
Copy link
Contributor

Task failure requires memory allocation.

@brson
Copy link
Contributor

brson commented Jul 24, 2014

I recall us not wanting generic types in extern fns. I'd like to have more light on this issue before merging.

@emberian
Copy link
Member

@brson The only situation it's useful in is passing a callback to a C function. It needs to have the right ABI, but you also want to be able to pass in a (fully instantiated, of course) pointer to a generic function.

One workaround that works today is extern "C" fn foo_for_int(...) { foo::<int>() }. Not nice, of course...

@rpjohnst
Copy link
Contributor Author

I just realized I've got multiple commits here where the earlier ones don't fully implement the feature. What's the policy on that? Do I need to squash them?

@nikomatsakis
Copy link
Contributor

I think it's fine for extern "C" fn's declared in Rust to be generic, but:

  • It should be an error to combine a generic fn with #[no_mangle], since we always mangle the name anyway.
  • I'd like to be sure that there are still tests ensuring that you can't reference an "external" fn (one not defined in the crate)
  • What happens if you reference an external Rust fn that has generics, is that supported? (Tested?)

@alexcrichton
Copy link
Member

@nikomatsakis, just to clarify, this is what you're thinking, right?

extern foo<T>(t: T) {} // ok

#[no_mangle]
extern foo2<T>(t: T) {} // error

extern {
    fn bar<T>(); // error
}

fn main() {
    // ok
    let _f: extern fn(int) = foo;
    let _f: extern fn(i16) = foo;

    // ok
    fn inner<T>(f: extern fn(T), t: T) { f(t) }
}

@rpjohnst it sounds like we'd like to move forward with this, but we're a little hesitant about the amount of testing here. We want to try to think of all the fun ways these can pop up and test them ahead of time to make sure nothing goes awry.

@rpjohnst
Copy link
Contributor Author

rpjohnst commented Aug 2, 2014

I reorganized the commits and added a few more tests that should cover all the mentioned cases now. The failed test is run-pass/spawn-stack-too-big, which is entirely unrelated and passes on my machine- is that a Travis failure rather than a test failure?

@lilyball
Copy link
Contributor

lilyball commented Aug 2, 2014

It's a Travis issue that all recent PRs have been hitting.

@alexcrichton
Copy link
Member

Thanks @rpjohnst, this looks great!

@rpjohnst
Copy link
Contributor Author

rpjohnst commented Aug 5, 2014

Do make check and Travis not do the run-make tests? The cross-crate generic extern is causing a fail in run-make/extern-fn-generic that didn't show up until bors (actually bors errored with missing pubs but fixing that exposed this error).

It looks like I need to skip generics in metadata::encode::encode_reachable_extern_fns and make sure the ABI information gets stored correctly in wherever it encodes generics.

@alexcrichton
Copy link
Member

Travis does not execute the run-make tests, but make check should.

Generic extern functions written in Rust have their names mangled, as well as their internal clownshoe __rust_abi functions. This allows e.g. specific monomorphizations of these functions to be used as callbacks.

Closes #12502.
@rpjohnst
Copy link
Contributor Author

rpjohnst commented Aug 6, 2014

It seems make check was not running the run-make tests on my machine when I used -j. The tests should all be fixed now.

@rpjohnst
Copy link
Contributor Author

rpjohnst commented Aug 7, 2014

The logs end with this:

test task::task_abort_no_kill_runtime ... ok

command timed out: 3600 seconds without output, attempting to kill

task_abort_no_kill_runtime has a call to timer::sleep(1000) but that shouldn't go 3600 seconds and it looks like it completed okay anyway. The next output on my machine (also linux 64-bit) is a bunch of ignored tests before it moves on to test libgreen.

Broken bot?

bors added a commit that referenced this pull request Aug 7, 2014
This allows for things like this:

    extern "C" fn callback<T>(t: T) { /* ... */ }
    extern "C" {
        fn take_callback(c: extern fn(i32));
    }

and later:

    take_callback(callback::<i32>);

Closes #12502.
@bors bors closed this Aug 7, 2014
@bors bors merged commit f7aadee into rust-lang:master Aug 7, 2014
@rpjohnst rpjohnst deleted the generic-foreign-fns branch August 7, 2014 23:14
bors added a commit that referenced this pull request Aug 9, 2014
…chton

Extend the changes from #16059 to the new generic foreign functions introduced by #15831.
bors added a commit to rust-lang-ci/rust that referenced this pull request Nov 13, 2023
fix: Fix docs path for derive macros

Fixes rust-lang#15831.

Not sure about `attr`, I don't think those are documented anyway. And many macros don't work because we pick the wrong path.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Generic foreign functions are not allowed
10 participants