Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions compiler/rustc_ast_passes/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,6 @@ ast_passes_body_in_extern = incorrect `{$kind}` inside `extern` block

ast_passes_bound_in_context = bounds on `type`s in {$ctx} have no effect

ast_passes_c_variadic_associated_function = associated functions cannot have a C variable argument list

ast_passes_c_variadic_bad_extern = `...` is not supported for `extern "{$abi}"` functions
.label = `extern "{$abi}"` because of this
.help = only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
Expand Down
7 changes: 1 addition & 6 deletions compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -696,7 +696,7 @@ impl<'a> AstValidator<'a> {

match fn_ctxt {
FnCtxt::Foreign => return,
FnCtxt::Free => match sig.header.ext {
FnCtxt::Free | FnCtxt::Assoc(_) => match sig.header.ext {
Extern::Implicit(_) => {
if !matches!(sig.header.safety, Safety::Unsafe(_)) {
self.dcx().emit_err(errors::CVariadicMustBeUnsafe {
Expand Down Expand Up @@ -726,11 +726,6 @@ impl<'a> AstValidator<'a> {
self.dcx().emit_err(err);
}
},
FnCtxt::Assoc(_) => {
// For now, C variable argument lists are unsupported in associated functions.
let err = errors::CVariadicAssociatedFunction { span: variadic_param.span };
self.dcx().emit_err(err);
}
}
}

Expand Down
7 changes: 0 additions & 7 deletions compiler/rustc_ast_passes/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -318,13 +318,6 @@ pub(crate) struct ExternItemAscii {
pub block: Span,
}

#[derive(Diagnostic)]
#[diag(ast_passes_c_variadic_associated_function)]
pub(crate) struct CVariadicAssociatedFunction {
#[primary_span]
pub span: Span,
}

#[derive(Diagnostic)]
#[diag(ast_passes_c_variadic_no_extern)]
#[help]
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_middle/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -823,6 +823,9 @@ impl DynCompatibilityViolation {
DynCompatibilityViolation::Method(name, MethodViolationCode::AsyncFn, _) => {
format!("method `{name}` is `async`").into()
}
DynCompatibilityViolation::Method(name, MethodViolationCode::CVariadic, _) => {
format!("method `{name}` is C-variadic").into()
}
DynCompatibilityViolation::Method(
name,
MethodViolationCode::WhereClauseReferencesSelf,
Expand Down Expand Up @@ -977,6 +980,9 @@ pub enum MethodViolationCode {
/// e.g., `fn foo<A>()`
Generic,

/// e.g., `fn (mut ap: ...)`
CVariadic,

/// the method's receiver (`self` argument) can't be dispatched on
UndispatchableReceiver(Option<Span>),
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,9 @@ fn virtual_call_violations_for_method<'tcx>(
if let Some(code) = contains_illegal_impl_trait_in_trait(tcx, method.def_id, sig.output()) {
errors.push(code);
}
if sig.skip_binder().c_variadic {
errors.push(MethodViolationCode::CVariadic);
}

// We can't monomorphize things like `fn foo<A>(...)`.
let own_counts = tcx.generics_of(method.def_id).own_counts();
Expand Down
45 changes: 45 additions & 0 deletions tests/ui/c-variadic/inherent-method.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//@ run-pass
#![feature(c_variadic)]

#[repr(transparent)]
struct S(i32);

impl S {
unsafe extern "C" fn associated_function(mut ap: ...) -> i32 {
unsafe { ap.arg() }
}

unsafe extern "C" fn method_owned(self, mut ap: ...) -> i32 {
self.0 + unsafe { ap.arg::<i32>() }
}

unsafe extern "C" fn method_ref(&self, mut ap: ...) -> i32 {
self.0 + unsafe { ap.arg::<i32>() }
}

unsafe extern "C" fn method_mut(&mut self, mut ap: ...) -> i32 {
self.0 + unsafe { ap.arg::<i32>() }
}

unsafe extern "C" fn fat_pointer(self: Box<Self>, mut ap: ...) -> i32 {
self.0 + unsafe { ap.arg::<i32>() }
}
}

fn main() {
unsafe {
assert_eq!(S::associated_function(32), 32);
assert_eq!(S(100).method_owned(32), 132);
assert_eq!(S(100).method_ref(32), 132);
assert_eq!(S(100).method_mut(32), 132);
assert_eq!(S::fat_pointer(Box::new(S(100)), 32), 132);

type Method<T> = unsafe extern "C" fn(T, ...) -> i32;

assert_eq!((S::associated_function as unsafe extern "C" fn(...) -> i32)(32), 32);
assert_eq!((S::method_owned as Method<_>)(S(100), 32), 132);
assert_eq!((S::method_ref as Method<_>)(&S(100), 32), 132);
assert_eq!((S::method_mut as Method<_>)(&mut S(100), 32), 132);
assert_eq!((S::fat_pointer as Method<_>)(Box::new(S(100)), 32), 132);
}
}
10 changes: 9 additions & 1 deletion tests/ui/c-variadic/not-async.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@
#![feature(c_variadic)]
#![crate_type = "lib"]

async unsafe extern "C" fn cannot_be_async(x: isize, ...) {}
async unsafe extern "C" fn fn_cannot_be_async(x: isize, ...) {}
//~^ ERROR functions cannot be both `async` and C-variadic
//~| ERROR hidden type for `impl Future<Output = ()>` captures lifetime that does not appear in bounds

struct S;

impl S {
async unsafe extern "C" fn method_cannot_be_async(x: isize, ...) {}
//~^ ERROR functions cannot be both `async` and C-variadic
//~| ERROR hidden type for `impl Future<Output = ()>` captures lifetime that does not appear in bounds
}
30 changes: 23 additions & 7 deletions tests/ui/c-variadic/not-async.stderr
Original file line number Diff line number Diff line change
@@ -1,19 +1,35 @@
error: functions cannot be both `async` and C-variadic
--> $DIR/not-async.rs:5:1
|
LL | async unsafe extern "C" fn cannot_be_async(x: isize, ...) {}
| ^^^^^ `async` because of this ^^^ C-variadic because of this
LL | async unsafe extern "C" fn fn_cannot_be_async(x: isize, ...) {}
| ^^^^^ `async` because of this ^^^ C-variadic because of this

error: functions cannot be both `async` and C-variadic
--> $DIR/not-async.rs:12:5
|
LL | async unsafe extern "C" fn method_cannot_be_async(x: isize, ...) {}
| ^^^^^ `async` because of this ^^^ C-variadic because of this

error[E0700]: hidden type for `impl Future<Output = ()>` captures lifetime that does not appear in bounds
--> $DIR/not-async.rs:5:59
--> $DIR/not-async.rs:5:62
|
LL | async unsafe extern "C" fn cannot_be_async(x: isize, ...) {}
| --------------------------------------------------------- ^^
LL | async unsafe extern "C" fn fn_cannot_be_async(x: isize, ...) {}
| ------------------------------------------------------------ ^^
| |
| opaque type defined here
|
= note: hidden type `{async fn body of cannot_be_async()}` captures lifetime `'_`
= note: hidden type `{async fn body of fn_cannot_be_async()}` captures lifetime `'_`

error[E0700]: hidden type for `impl Future<Output = ()>` captures lifetime that does not appear in bounds
--> $DIR/not-async.rs:12:70
|
LL | async unsafe extern "C" fn method_cannot_be_async(x: isize, ...) {}
| ---------------------------------------------------------------- ^^
| |
| opaque type defined here
|
= note: hidden type `{async fn body of S::method_cannot_be_async()}` captures lifetime `'_`

error: aborting due to 2 previous errors
error: aborting due to 4 previous errors

For more information about this error, try `rustc --explain E0700`.
35 changes: 35 additions & 0 deletions tests/ui/c-variadic/not-dyn-compatible.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Traits where a method is c-variadic are not dyn compatible.
//
// Creating a function pointer from a method on an `&dyn T` value creates a ReifyShim.
// This shim cannot reliably forward C-variadic arguments. Thus the trait as a whole
// is dyn-incompatible to prevent invalid shims from being created.
#![feature(c_variadic)]

#[repr(transparent)]
struct Struct(u64);

trait Trait {
fn get(&self) -> u64;

unsafe extern "C" fn dyn_method_ref(&self, mut ap: ...) -> u64 {
self.get() + unsafe { ap.arg::<u64>() }
}
}

impl Trait for Struct {
fn get(&self) -> u64 {
self.0
}
}

fn main() {
unsafe {
let dyn_object: &dyn Trait = &Struct(64);
//~^ ERROR the trait `Trait` is not dyn compatible
assert_eq!(dyn_object.dyn_method_ref(100), 164);
assert_eq!(
(Trait::dyn_method_ref as unsafe extern "C" fn(_, ...) -> u64)(dyn_object, 100),
164
);
}
}
21 changes: 21 additions & 0 deletions tests/ui/c-variadic/not-dyn-compatible.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
error[E0038]: the trait `Trait` is not dyn compatible
--> $DIR/not-dyn-compatible.rs:27:30
|
LL | let dyn_object: &dyn Trait = &Struct(64);
| ^^^^^ `Trait` is not dyn compatible
|
note: for a trait to be dyn compatible it needs to allow building a vtable
for more information, visit <https://doc.rust-lang.org/reference/items/traits.html#dyn-compatibility>
--> $DIR/not-dyn-compatible.rs:14:26
|
LL | trait Trait {
| ----- this trait is not dyn compatible...
...
LL | unsafe extern "C" fn dyn_method_ref(&self, mut ap: ...) -> u64 {
| ^^^^^^^^^^^^^^ ...because method `dyn_method_ref` is C-variadic
= help: consider moving `dyn_method_ref` to another trait
= help: only type `Struct` implements `Trait`; consider using it directly instead.

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0038`.
73 changes: 73 additions & 0 deletions tests/ui/c-variadic/trait-method.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
//@ run-pass
#![feature(c_variadic)]

#[repr(transparent)]
struct Struct(i32);

impl Struct {
unsafe extern "C" fn associated_function(mut ap: ...) -> i32 {
unsafe { ap.arg() }
}

unsafe extern "C" fn method(&self, mut ap: ...) -> i32 {
self.0 + unsafe { ap.arg::<i32>() }
}
}

trait Trait: Sized {
fn get(&self) -> i32;

unsafe extern "C" fn trait_associated_function(mut ap: ...) -> i32 {
unsafe { ap.arg() }
}

unsafe extern "C" fn trait_method_owned(self, mut ap: ...) -> i32 {
self.get() + unsafe { ap.arg::<i32>() }
}

unsafe extern "C" fn trait_method_ref(&self, mut ap: ...) -> i32 {
self.get() + unsafe { ap.arg::<i32>() }
}

unsafe extern "C" fn trait_method_mut(&mut self, mut ap: ...) -> i32 {
self.get() + unsafe { ap.arg::<i32>() }
}

unsafe extern "C" fn trait_fat_pointer(self: Box<Self>, mut ap: ...) -> i32 {
self.get() + unsafe { ap.arg::<i32>() }
}
}

impl Trait for Struct {
fn get(&self) -> i32 {
self.0
}
}

fn main() {
unsafe {
assert_eq!(Struct::associated_function(32), 32);
assert_eq!(Struct(100).method(32), 132);

assert_eq!(Struct::trait_associated_function(32), 32);
assert_eq!(Struct(100).trait_method_owned(32), 132);
assert_eq!(Struct(100).trait_method_ref(32), 132);
assert_eq!(Struct(100).trait_method_mut(32), 132);
assert_eq!(Struct::trait_fat_pointer(Box::new(Struct(100)), 32), 132);

assert_eq!(<Struct as Trait>::trait_associated_function(32), 32);
assert_eq!(Trait::trait_method_owned(Struct(100), 32), 132);
assert_eq!(Trait::trait_method_ref(&Struct(100), 32), 132);
assert_eq!(Trait::trait_method_mut(&mut Struct(100), 32), 132);
assert_eq!(Trait::trait_fat_pointer(Box::new(Struct(100)), 32), 132);

type Associated = unsafe extern "C" fn(...) -> i32;
type Method<T> = unsafe extern "C" fn(T, ...) -> i32;

assert_eq!((Struct::trait_associated_function as Associated)(32), 32);
assert_eq!((Struct::trait_method_owned as Method<_>)(Struct(100), 32), 132);
assert_eq!((Struct::trait_method_ref as Method<_>)(&Struct(100), 32), 132);
assert_eq!((Struct::trait_method_mut as Method<_>)(&mut Struct(100), 32), 132);
assert_eq!((Struct::trait_fat_pointer as Method<_>)(Box::new(Struct(100)), 32), 132);
}
}
Loading
Loading