From ceac273e60447014038e511e5af43841c359547c Mon Sep 17 00:00:00 2001 From: David Wood Date: Sat, 13 Jun 2020 19:37:25 +0100 Subject: [PATCH] wf: check foreign fn decls for well-formedness This commit extends current well-formedness checking to apply to foreign function declarations, re-using the existing machinery for regular functions. In doing this, later parts of the compiler (such as the `improper_ctypes` lint) can rely on being operations not failing as a result of invalid code which would normally be caught earlier. Signed-off-by: David Wood --- src/librustc_typeck/check/wfcheck.rs | 48 ++++++++------- src/test/ui/lint/lint-ctypes.rs | 3 +- src/test/ui/lint/lint-ctypes.stderr | 62 ++++++++++---------- src/test/ui/wf/wf-foreign-fn-decl-ret.rs | 18 ++++++ src/test/ui/wf/wf-foreign-fn-decl-ret.stderr | 18 ++++++ 5 files changed, 97 insertions(+), 52 deletions(-) create mode 100644 src/test/ui/wf/wf-foreign-fn-decl-ret.rs create mode 100644 src/test/ui/wf/wf-foreign-fn-decl-ret.stderr diff --git a/src/librustc_typeck/check/wfcheck.rs b/src/librustc_typeck/check/wfcheck.rs index 845a4fcafc224..dabae6cbc4137 100644 --- a/src/librustc_typeck/check/wfcheck.rs +++ b/src/librustc_typeck/check/wfcheck.rs @@ -15,7 +15,7 @@ use rustc_middle::ty::{ self, AdtKind, GenericParamDefKind, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness, }; use rustc_session::parse::feature_err; -use rustc_span::symbol::{sym, Symbol}; +use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::Span; use rustc_trait_selection::opaque_types::may_define_opaque_type; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; @@ -142,8 +142,8 @@ pub fn check_item_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) { _ => unreachable!(), } } - hir::ItemKind::Fn(..) => { - check_item_fn(tcx, item); + hir::ItemKind::Fn(ref sig, ..) => { + check_item_fn(tcx, item.hir_id, item.ident, item.span, sig.decl); } hir::ItemKind::Static(ref ty, ..) => { check_item_type(tcx, item.hir_id, ty.span, false); @@ -153,8 +153,14 @@ pub fn check_item_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) { } hir::ItemKind::ForeignMod(ref module) => { for it in module.items.iter() { - if let hir::ForeignItemKind::Static(ref ty, ..) = it.kind { - check_item_type(tcx, it.hir_id, ty.span, true); + match it.kind { + hir::ForeignItemKind::Fn(ref decl, ..) => { + check_item_fn(tcx, it.hir_id, it.ident, it.span, decl) + } + hir::ForeignItemKind::Static(ref ty, ..) => { + check_item_type(tcx, it.hir_id, ty.span, true) + } + hir::ForeignItemKind::Type => (), } } } @@ -303,7 +309,7 @@ fn check_associated_item( fcx, item.ident.span, sig, - hir_sig, + hir_sig.decl, item.def_id, &mut implied_bounds, ); @@ -564,22 +570,24 @@ fn check_associated_type_defaults(fcx: &FnCtxt<'_, '_>, trait_def_id: DefId) { } } -fn check_item_fn(tcx: TyCtxt<'_>, item: &hir::Item<'_>) { - for_item(tcx, item).with_fcx(|fcx, tcx| { - let def_id = fcx.tcx.hir().local_def_id(item.hir_id); +fn check_item_fn( + tcx: TyCtxt<'_>, + item_id: hir::HirId, + ident: Ident, + span: Span, + decl: &hir::FnDecl<'_>, +) { + for_id(tcx, item_id, span).with_fcx(|fcx, tcx| { + let def_id = fcx.tcx.hir().local_def_id(item_id); let sig = fcx.tcx.fn_sig(def_id); - let sig = fcx.normalize_associated_types_in(item.span, &sig); + let sig = fcx.normalize_associated_types_in(span, &sig); let mut implied_bounds = vec![]; - let hir_sig = match &item.kind { - ItemKind::Fn(sig, ..) => sig, - _ => bug!("expected `ItemKind::Fn`, found `{:?}`", item.kind), - }; check_fn_or_method( tcx, fcx, - item.ident.span, + ident.span, sig, - hir_sig, + decl, def_id.to_def_id(), &mut implied_bounds, ); @@ -835,28 +843,28 @@ fn check_fn_or_method<'fcx, 'tcx>( fcx: &FnCtxt<'fcx, 'tcx>, span: Span, sig: ty::PolyFnSig<'tcx>, - hir_sig: &hir::FnSig<'_>, + hir_decl: &hir::FnDecl<'_>, def_id: DefId, implied_bounds: &mut Vec>, ) { let sig = fcx.normalize_associated_types_in(span, &sig); let sig = fcx.tcx.liberate_late_bound_regions(def_id, &sig); - for (&input_ty, span) in sig.inputs().iter().zip(hir_sig.decl.inputs.iter().map(|t| t.span)) { + for (&input_ty, span) in sig.inputs().iter().zip(hir_decl.inputs.iter().map(|t| t.span)) { fcx.register_wf_obligation(input_ty.into(), span, ObligationCauseCode::MiscObligation); } implied_bounds.extend(sig.inputs()); fcx.register_wf_obligation( sig.output().into(), - hir_sig.decl.output.span(), + hir_decl.output.span(), ObligationCauseCode::ReturnType, ); // FIXME(#25759) return types should not be implied bounds implied_bounds.push(sig.output()); - check_where_clauses(tcx, fcx, span, def_id, Some((sig.output(), hir_sig.decl.output.span()))); + check_where_clauses(tcx, fcx, span, def_id, Some((sig.output(), hir_decl.output.span()))); } /// Checks "defining uses" of opaque `impl Trait` types to ensure that they meet the restrictions diff --git a/src/test/ui/lint/lint-ctypes.rs b/src/test/ui/lint/lint-ctypes.rs index bdf95350c7045..f485766bcd34e 100644 --- a/src/test/ui/lint/lint-ctypes.rs +++ b/src/test/ui/lint/lint-ctypes.rs @@ -7,6 +7,7 @@ extern crate libc; use std::marker::PhantomData; +trait Bar { } trait Mirror { type It: ?Sized; } impl Mirror for T { type It = Self; } #[repr(C)] @@ -53,7 +54,7 @@ extern { pub fn char_type(p: char); //~ ERROR uses type `char` pub fn i128_type(p: i128); //~ ERROR uses type `i128` pub fn u128_type(p: u128); //~ ERROR uses type `u128` - pub fn trait_type(p: &dyn Clone); //~ ERROR uses type `dyn std::clone::Clone` + pub fn trait_type(p: &dyn Bar); //~ ERROR uses type `dyn Bar` pub fn tuple_type(p: (i32, i32)); //~ ERROR uses type `(i32, i32)` pub fn tuple_type2(p: I32Pair); //~ ERROR uses type `(i32, i32)` pub fn zero_size(p: ZeroSize); //~ ERROR uses type `ZeroSize` diff --git a/src/test/ui/lint/lint-ctypes.stderr b/src/test/ui/lint/lint-ctypes.stderr index 13b9adca3f9f5..a54226a7fc4a2 100644 --- a/src/test/ui/lint/lint-ctypes.stderr +++ b/src/test/ui/lint/lint-ctypes.stderr @@ -1,5 +1,5 @@ error: `extern` block uses type `Foo`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:46:28 + --> $DIR/lint-ctypes.rs:47:28 | LL | pub fn ptr_type1(size: *const Foo); | ^^^^^^^^^^ not FFI-safe @@ -12,13 +12,13 @@ LL | #![deny(improper_ctypes)] = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct = note: this struct has unspecified layout note: the type is defined here - --> $DIR/lint-ctypes.rs:24:1 + --> $DIR/lint-ctypes.rs:25:1 | LL | pub struct Foo; | ^^^^^^^^^^^^^^^ error: `extern` block uses type `Foo`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:47:28 + --> $DIR/lint-ctypes.rs:48:28 | LL | pub fn ptr_type2(size: *const Foo); | ^^^^^^^^^^ not FFI-safe @@ -26,13 +26,13 @@ LL | pub fn ptr_type2(size: *const Foo); = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct = note: this struct has unspecified layout note: the type is defined here - --> $DIR/lint-ctypes.rs:24:1 + --> $DIR/lint-ctypes.rs:25:1 | LL | pub struct Foo; | ^^^^^^^^^^^^^^^ error: `extern` block uses type `[u32]`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:48:26 + --> $DIR/lint-ctypes.rs:49:26 | LL | pub fn slice_type(p: &[u32]); | ^^^^^^ not FFI-safe @@ -41,7 +41,7 @@ LL | pub fn slice_type(p: &[u32]); = note: slices have no C equivalent error: `extern` block uses type `str`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:49:24 + --> $DIR/lint-ctypes.rs:50:24 | LL | pub fn str_type(p: &str); | ^^^^ not FFI-safe @@ -50,7 +50,7 @@ LL | pub fn str_type(p: &str); = note: string slices have no C equivalent error: `extern` block uses type `std::boxed::Box`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:50:24 + --> $DIR/lint-ctypes.rs:51:24 | LL | pub fn box_type(p: Box); | ^^^^^^^^ not FFI-safe @@ -59,7 +59,7 @@ LL | pub fn box_type(p: Box); = note: this struct has unspecified layout error: `extern` block uses type `std::option::Option>`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:51:28 + --> $DIR/lint-ctypes.rs:52:28 | LL | pub fn opt_box_type(p: Option>); | ^^^^^^^^^^^^^^^^ not FFI-safe @@ -68,7 +68,7 @@ LL | pub fn opt_box_type(p: Option>); = note: enum has no representation hint error: `extern` block uses type `char`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:53:25 + --> $DIR/lint-ctypes.rs:54:25 | LL | pub fn char_type(p: char); | ^^^^ not FFI-safe @@ -77,7 +77,7 @@ LL | pub fn char_type(p: char); = note: the `char` type has no C equivalent error: `extern` block uses type `i128`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:54:25 + --> $DIR/lint-ctypes.rs:55:25 | LL | pub fn i128_type(p: i128); | ^^^^ not FFI-safe @@ -85,23 +85,23 @@ LL | pub fn i128_type(p: i128); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:55:25 + --> $DIR/lint-ctypes.rs:56:25 | LL | pub fn u128_type(p: u128); | ^^^^ not FFI-safe | = note: 128-bit integers don't currently have a known stable ABI -error: `extern` block uses type `dyn std::clone::Clone`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:56:26 +error: `extern` block uses type `dyn Bar`, which is not FFI-safe + --> $DIR/lint-ctypes.rs:57:26 | -LL | pub fn trait_type(p: &dyn Clone); - | ^^^^^^^^^^ not FFI-safe +LL | pub fn trait_type(p: &dyn Bar); + | ^^^^^^^^ not FFI-safe | = note: trait objects have no C equivalent error: `extern` block uses type `(i32, i32)`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:57:26 + --> $DIR/lint-ctypes.rs:58:26 | LL | pub fn tuple_type(p: (i32, i32)); | ^^^^^^^^^^ not FFI-safe @@ -110,7 +110,7 @@ LL | pub fn tuple_type(p: (i32, i32)); = note: tuples have unspecified layout error: `extern` block uses type `(i32, i32)`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:58:27 + --> $DIR/lint-ctypes.rs:59:27 | LL | pub fn tuple_type2(p: I32Pair); | ^^^^^^^ not FFI-safe @@ -119,7 +119,7 @@ LL | pub fn tuple_type2(p: I32Pair); = note: tuples have unspecified layout error: `extern` block uses type `ZeroSize`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:59:25 + --> $DIR/lint-ctypes.rs:60:25 | LL | pub fn zero_size(p: ZeroSize); | ^^^^^^^^ not FFI-safe @@ -127,26 +127,26 @@ LL | pub fn zero_size(p: ZeroSize); = help: consider adding a member to this struct = note: this struct has no fields note: the type is defined here - --> $DIR/lint-ctypes.rs:20:1 + --> $DIR/lint-ctypes.rs:21:1 | LL | pub struct ZeroSize; | ^^^^^^^^^^^^^^^^^^^^ error: `extern` block uses type `ZeroSizeWithPhantomData`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:60:33 + --> $DIR/lint-ctypes.rs:61:33 | LL | pub fn zero_size_phantom(p: ZeroSizeWithPhantomData); | ^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe | = note: composed only of `PhantomData` note: the type is defined here - --> $DIR/lint-ctypes.rs:43:1 + --> $DIR/lint-ctypes.rs:44:1 | LL | pub struct ZeroSizeWithPhantomData(::std::marker::PhantomData); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `extern` block uses type `std::marker::PhantomData`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:63:12 + --> $DIR/lint-ctypes.rs:64:12 | LL | -> ::std::marker::PhantomData; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -154,7 +154,7 @@ LL | -> ::std::marker::PhantomData; = note: composed only of `PhantomData` error: `extern` block uses type `fn()`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:64:23 + --> $DIR/lint-ctypes.rs:65:23 | LL | pub fn fn_type(p: RustFn); | ^^^^^^ not FFI-safe @@ -163,7 +163,7 @@ LL | pub fn fn_type(p: RustFn); = note: this function pointer has Rust-specific calling convention error: `extern` block uses type `fn()`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:65:24 + --> $DIR/lint-ctypes.rs:66:24 | LL | pub fn fn_type2(p: fn()); | ^^^^ not FFI-safe @@ -172,7 +172,7 @@ LL | pub fn fn_type2(p: fn()); = note: this function pointer has Rust-specific calling convention error: `extern` block uses type `std::boxed::Box`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:66:28 + --> $DIR/lint-ctypes.rs:67:28 | LL | pub fn fn_contained(p: RustBadRet); | ^^^^^^^^^^ not FFI-safe @@ -181,7 +181,7 @@ LL | pub fn fn_contained(p: RustBadRet); = note: this struct has unspecified layout error: `extern` block uses type `i128`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:67:32 + --> $DIR/lint-ctypes.rs:68:32 | LL | pub fn transparent_i128(p: TransparentI128); | ^^^^^^^^^^^^^^^ not FFI-safe @@ -189,7 +189,7 @@ LL | pub fn transparent_i128(p: TransparentI128); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `str`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:68:31 + --> $DIR/lint-ctypes.rs:69:31 | LL | pub fn transparent_str(p: TransparentStr); | ^^^^^^^^^^^^^^ not FFI-safe @@ -198,7 +198,7 @@ LL | pub fn transparent_str(p: TransparentStr); = note: string slices have no C equivalent error: `extern` block uses type `std::boxed::Box`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:69:30 + --> $DIR/lint-ctypes.rs:70:30 | LL | pub fn transparent_fn(p: TransparentBadFn); | ^^^^^^^^^^^^^^^^ not FFI-safe @@ -207,7 +207,7 @@ LL | pub fn transparent_fn(p: TransparentBadFn); = note: this struct has unspecified layout error: `extern` block uses type `[u8; 8]`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:70:27 + --> $DIR/lint-ctypes.rs:71:27 | LL | pub fn raw_array(arr: [u8; 8]); | ^^^^^^^ not FFI-safe @@ -216,7 +216,7 @@ LL | pub fn raw_array(arr: [u8; 8]); = note: passing raw arrays by value is not FFI-safe error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:72:34 + --> $DIR/lint-ctypes.rs:73:34 | LL | pub static static_u128_type: u128; | ^^^^ not FFI-safe @@ -224,7 +224,7 @@ LL | pub static static_u128_type: u128; = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:73:40 + --> $DIR/lint-ctypes.rs:74:40 | LL | pub static static_u128_array_type: [u128; 16]; | ^^^^^^^^^^ not FFI-safe diff --git a/src/test/ui/wf/wf-foreign-fn-decl-ret.rs b/src/test/ui/wf/wf-foreign-fn-decl-ret.rs new file mode 100644 index 0000000000000..b9d956c056869 --- /dev/null +++ b/src/test/ui/wf/wf-foreign-fn-decl-ret.rs @@ -0,0 +1,18 @@ +pub trait Unsatisfied {} + +#[repr(transparent)] +pub struct Bar(T); + +pub trait Foo { + type Assoc; +} + +extern "C" { + pub fn lint_me() -> <() as Foo>::Assoc; + //~^ ERROR: the trait bound `(): Foo` is not satisfied [E0277] + + pub fn lint_me_aswell() -> Bar; + //~^ ERROR: the trait bound `u32: Unsatisfied` is not satisfied [E0277] +} + +fn main() {} diff --git a/src/test/ui/wf/wf-foreign-fn-decl-ret.stderr b/src/test/ui/wf/wf-foreign-fn-decl-ret.stderr new file mode 100644 index 0000000000000..9081b7929d935 --- /dev/null +++ b/src/test/ui/wf/wf-foreign-fn-decl-ret.stderr @@ -0,0 +1,18 @@ +error[E0277]: the trait bound `(): Foo` is not satisfied + --> $DIR/wf-foreign-fn-decl-ret.rs:11:5 + | +LL | pub fn lint_me() -> <() as Foo>::Assoc; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `()` + +error[E0277]: the trait bound `u32: Unsatisfied` is not satisfied + --> $DIR/wf-foreign-fn-decl-ret.rs:14:32 + | +LL | pub struct Bar(T); + | ----------- required by this bound in `Bar` +... +LL | pub fn lint_me_aswell() -> Bar; + | ^^^^^^^^ the trait `Unsatisfied` is not implemented for `u32` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`.