From 3f661d25f9ce3b5ac6139c8d8bf4614b55dca5be Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Mon, 11 May 2020 10:29:05 +0200 Subject: [PATCH 01/56] borrowck `DefId` -> `LocalDefId` --- .../diagnostics/conflict_errors.rs | 89 +++++++++---------- .../borrow_check/diagnostics/move_errors.rs | 2 +- .../diagnostics/mutability_errors.rs | 2 +- .../borrow_check/diagnostics/region_errors.rs | 2 +- .../borrow_check/diagnostics/region_name.rs | 9 +- src/librustc_mir/borrow_check/mod.rs | 14 ++- src/librustc_mir/borrow_check/nll.rs | 8 +- 7 files changed, 59 insertions(+), 67 deletions(-) diff --git a/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs b/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs index 5f1c0911da2b..d0050f801fc6 100644 --- a/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs +++ b/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs @@ -214,7 +214,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let generics = tcx.generics_of(self.mir_def_id); let param = generics.type_param(¶m_ty, tcx); if let Some(generics) = - tcx.hir().get_generics(tcx.closure_base_def_id(self.mir_def_id)) + tcx.hir().get_generics(tcx.closure_base_def_id(self.mir_def_id.to_def_id())) { suggest_constraining_type_param( tcx, @@ -865,49 +865,42 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { format!("`{}` would have to be valid for `{}`...", name, region_name), ); - if let Some(def_id) = self.mir_def_id.as_local() { - let fn_hir_id = self.infcx.tcx.hir().as_local_hir_id(def_id); - err.span_label( - drop_span, - format!( - "...but `{}` will be dropped here, when the {} returns", - name, - self.infcx - .tcx - .hir() - .opt_name(fn_hir_id) - .map(|name| format!("function `{}`", name)) - .unwrap_or_else(|| { - match &self - .infcx - .tcx - .typeck_tables_of(def_id) - .node_type(fn_hir_id) - .kind - { - ty::Closure(..) => "enclosing closure", - ty::Generator(..) => "enclosing generator", - kind => bug!("expected closure or generator, found {:?}", kind), - } - .to_string() - }) - ), - ); + let fn_hir_id = self.infcx.tcx.hir().as_local_hir_id(self.mir_def_id); + err.span_label( + drop_span, + format!( + "...but `{}` will be dropped here, when the {} returns", + name, + self.infcx + .tcx + .hir() + .opt_name(fn_hir_id) + .map(|name| format!("function `{}`", name)) + .unwrap_or_else(|| { + match &self + .infcx + .tcx + .typeck_tables_of(self.mir_def_id) + .node_type(fn_hir_id) + .kind + { + ty::Closure(..) => "enclosing closure", + ty::Generator(..) => "enclosing generator", + kind => bug!("expected closure or generator, found {:?}", kind), + } + .to_string() + }) + ), + ); - err.note( - "functions cannot return a borrow to data owned within the function's scope, \ - functions can only return borrows to data passed as arguments", - ); - err.note( - "to learn more, visit ", - ); - } else { - err.span_label( - drop_span, - format!("...but `{}` dropped here while still borrowed", name), - ); - } + err.note( + "functions cannot return a borrow to data owned within the function's scope, \ + functions can only return borrows to data passed as arguments", + ); + err.note( + "to learn more, visit ", + ); if let BorrowExplanation::MustBeValidFor { .. } = explanation { } else { @@ -1237,7 +1230,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ) -> DiagnosticBuilder<'cx> { let tcx = self.infcx.tcx; - let (_, escapes_from) = tcx.article_and_description(self.mir_def_id); + let (_, escapes_from) = tcx.article_and_description(self.mir_def_id.to_def_id()); let mut err = borrowck_errors::borrowed_data_escapes_closure(tcx, escape_span, escapes_from); @@ -1572,14 +1565,16 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ) -> Option> { // Define a fallback for when we can't match a closure. let fallback = || { - let is_closure = self.infcx.tcx.is_closure(self.mir_def_id); + let is_closure = self.infcx.tcx.is_closure(self.mir_def_id.to_def_id()); if is_closure { None } else { let ty = self.infcx.tcx.type_of(self.mir_def_id); match ty.kind { - ty::FnDef(_, _) | ty::FnPtr(_) => self - .annotate_fn_sig(self.mir_def_id, self.infcx.tcx.fn_sig(self.mir_def_id)), + ty::FnDef(_, _) | ty::FnPtr(_) => self.annotate_fn_sig( + self.mir_def_id.to_def_id(), + self.infcx.tcx.fn_sig(self.mir_def_id), + ), _ => None, } } diff --git a/src/librustc_mir/borrow_check/diagnostics/move_errors.rs b/src/librustc_mir/borrow_check/diagnostics/move_errors.rs index 67254811ec52..b49e4187fb81 100644 --- a/src/librustc_mir/borrow_check/diagnostics/move_errors.rs +++ b/src/librustc_mir/borrow_check/diagnostics/move_errors.rs @@ -331,7 +331,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { self.cannot_move_out_of_interior_noncopy(span, ty, None) } ty::Closure(def_id, closure_substs) - if def_id == self.mir_def_id && upvar_field.is_some() => + if def_id.as_local() == Some(self.mir_def_id) && upvar_field.is_some() => { let closure_kind_ty = closure_substs.as_closure().kind_ty(); let closure_kind = closure_kind_ty.to_opt_closure_kind(); diff --git a/src/librustc_mir/borrow_check/diagnostics/mutability_errors.rs b/src/librustc_mir/borrow_check/diagnostics/mutability_errors.rs index 402eac47c462..e04ed8b83deb 100644 --- a/src/librustc_mir/borrow_check/diagnostics/mutability_errors.rs +++ b/src/librustc_mir/borrow_check/diagnostics/mutability_errors.rs @@ -492,7 +492,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { err.span_label(sp, format!("cannot {}", act)); let hir = self.infcx.tcx.hir(); - let closure_id = hir.as_local_hir_id(self.mir_def_id.expect_local()); + let closure_id = hir.as_local_hir_id(self.mir_def_id); let fn_call_id = hir.get_parent_node(closure_id); let node = hir.get(fn_call_id); let item_id = hir.get_parent_item(fn_call_id); diff --git a/src/librustc_mir/borrow_check/diagnostics/region_errors.rs b/src/librustc_mir/borrow_check/diagnostics/region_errors.rs index 98c0542f9c0d..f24d66873994 100644 --- a/src/librustc_mir/borrow_check/diagnostics/region_errors.rs +++ b/src/librustc_mir/borrow_check/diagnostics/region_errors.rs @@ -502,7 +502,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let mut diag = self.infcx.tcx.sess.struct_span_err(*span, "lifetime may not live long enough"); - let (_, mir_def_name) = self.infcx.tcx.article_and_description(self.mir_def_id); + let (_, mir_def_name) = self.infcx.tcx.article_and_description(self.mir_def_id.to_def_id()); let fr_name = self.give_region_a_name(*fr).unwrap(); fr_name.highlight_region_name(&mut diag); diff --git a/src/librustc_mir/borrow_check/diagnostics/region_name.rs b/src/librustc_mir/borrow_check/diagnostics/region_name.rs index 37e2e0475048..aee825a81138 100644 --- a/src/librustc_mir/borrow_check/diagnostics/region_name.rs +++ b/src/librustc_mir/borrow_check/diagnostics/region_name.rs @@ -237,8 +237,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { } ty::BoundRegion::BrEnv => { - let mir_hir_id = - self.infcx.tcx.hir().as_local_hir_id(self.mir_def_id.expect_local()); + let mir_hir_id = self.infcx.tcx.hir().as_local_hir_id(self.mir_def_id); let def_ty = self.regioncx.universal_regions().defining_ty; if let DefiningTy::Closure(_, substs) = def_ty { @@ -324,7 +323,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { argument_ty: Ty<'tcx>, argument_index: usize, ) -> Option { - let mir_hir_id = self.infcx.tcx.hir().as_local_hir_id(self.mir_def_id.as_local()?); + let mir_hir_id = self.infcx.tcx.hir().as_local_hir_id(self.mir_def_id); let fn_decl = self.infcx.tcx.hir().fn_decl_by_hir_id(mir_hir_id)?; let argument_hir_ty: &hir::Ty<'_> = fn_decl.inputs.get(argument_index)?; match argument_hir_ty.kind { @@ -635,7 +634,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { highlight.highlighting_region_vid(fr, *self.next_region_name.try_borrow().unwrap()); let type_name = self.infcx.extract_type_name(&return_ty, Some(highlight)).0; - let mir_hir_id = tcx.hir().as_local_hir_id(self.mir_def_id.expect_local()); + let mir_hir_id = tcx.hir().as_local_hir_id(self.mir_def_id); let (return_span, mir_description) = match tcx.hir().get(mir_hir_id) { hir::Node::Expr(hir::Expr { @@ -687,7 +686,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { highlight.highlighting_region_vid(fr, *self.next_region_name.try_borrow().unwrap()); let type_name = self.infcx.extract_type_name(&yield_ty, Some(highlight)).0; - let mir_hir_id = tcx.hir().as_local_hir_id(self.mir_def_id.expect_local()); + let mir_hir_id = tcx.hir().as_local_hir_id(self.mir_def_id); let yield_span = match tcx.hir().get(mir_hir_id) { hir::Node::Expr(hir::Expr { diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index d3ab7df817b3..baba3cb1babc 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -4,10 +4,8 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::graph::dominators::Dominators; use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorReported}; use rustc_hir as hir; -use rustc_hir::{ - def_id::{DefId, LocalDefId}, - HirId, Node, -}; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::{HirId, Node}; use rustc_index::bit_set::BitSet; use rustc_index::vec::IndexVec; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; @@ -174,7 +172,7 @@ fn do_mir_borrowck<'a, 'tcx>( let mut body = input_body.clone(); let mut promoted = input_promoted.clone(); let free_regions = - nll::replace_regions_in_mir(infcx, def_id.to_def_id(), param_env, &mut body, &mut promoted); + nll::replace_regions_in_mir(infcx, def_id, param_env, &mut body, &mut promoted); let body = &body; // no further changes let location_table = &LocationTable::new(&body); @@ -275,7 +273,7 @@ fn do_mir_borrowck<'a, 'tcx>( let mut promoted_mbcx = MirBorrowckCtxt { infcx, body: promoted_body, - mir_def_id: def_id.to_def_id(), + mir_def_id: def_id, move_data: &move_data, location_table: &LocationTable::new(promoted_body), movable_generator, @@ -307,7 +305,7 @@ fn do_mir_borrowck<'a, 'tcx>( let mut mbcx = MirBorrowckCtxt { infcx, body, - mir_def_id: def_id.to_def_id(), + mir_def_id: def_id, move_data: &mdpe.move_data, location_table, movable_generator, @@ -459,7 +457,7 @@ fn do_mir_borrowck<'a, 'tcx>( crate struct MirBorrowckCtxt<'cx, 'tcx> { crate infcx: &'cx InferCtxt<'cx, 'tcx>, body: &'cx Body<'tcx>, - mir_def_id: DefId, + mir_def_id: LocalDefId, move_data: &'cx MoveData<'tcx>, /// Map from MIR `Location` to `LocationIndex`; created diff --git a/src/librustc_mir/borrow_check/nll.rs b/src/librustc_mir/borrow_check/nll.rs index 29636a067092..eb453c4bb7b3 100644 --- a/src/librustc_mir/borrow_check/nll.rs +++ b/src/librustc_mir/borrow_check/nll.rs @@ -2,7 +2,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_errors::Diagnostic; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_index::vec::IndexVec; use rustc_infer::infer::InferCtxt; use rustc_middle::mir::{ @@ -58,7 +58,7 @@ crate struct NllOutput<'tcx> { /// `compute_regions`. pub(in crate::borrow_check) fn replace_regions_in_mir<'cx, 'tcx>( infcx: &InferCtxt<'cx, 'tcx>, - def_id: DefId, + def_id: LocalDefId, param_env: ty::ParamEnv<'tcx>, body: &mut Body<'tcx>, promoted: &mut IndexVec>, @@ -66,12 +66,12 @@ pub(in crate::borrow_check) fn replace_regions_in_mir<'cx, 'tcx>( debug!("replace_regions_in_mir(def_id={:?})", def_id); // Compute named region information. This also renumbers the inputs/outputs. - let universal_regions = UniversalRegions::new(infcx, def_id.expect_local(), param_env); + let universal_regions = UniversalRegions::new(infcx, def_id, param_env); // Replace all remaining regions with fresh inference variables. renumber::renumber_mir(infcx, body, promoted); - let source = MirSource::item(def_id); + let source = MirSource::item(def_id.to_def_id()); mir_util::dump_mir(infcx.tcx, None, "renumber", &0, source, body, |_, _| Ok(())); universal_regions From a8ed9aa9f016c60f5fa55326f1f6a30e2be9d83e Mon Sep 17 00:00:00 2001 From: Ivan Tham Date: Mon, 13 Apr 2020 21:41:45 +0800 Subject: [PATCH 02/56] impl From<[T; N]> for Box<[T]> Based on https://github.com/rust-lang/rust/pull/68692 --- src/liballoc/boxed.rs | 19 ++++++++++++++ .../alloc-traits-impls-length-32.rs | 4 +++ .../alloc-types-no-impls-length-33.rs | 2 ++ .../alloc-types-no-impls-length-33.stderr | 25 ++++++++++++++----- 4 files changed, 44 insertions(+), 6 deletions(-) diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index 8ef6090c743a..8cc6f04c0653 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -865,6 +865,25 @@ impl From> for Box<[u8]> { } } +#[stable(feature = "box_from_array", since = "1.45.0")] +impl From<[T; N]> for Box<[T]> +where + [T; N]: LengthAtMost32, +{ + /// Converts a `[T; N]` into a `Box<[T]>` + /// + /// This conversion moves the array to newly heap-allocated memory. + /// + /// # Examples + /// ```rust + /// let boxed: Box<[u8]> = Box::from([4, 2]); + /// println!("{:?}", boxed); + /// ``` + fn from(array: [T; N]) -> Box<[T]> { + box array + } +} + #[stable(feature = "boxed_slice_try_from", since = "1.43.0")] impl TryFrom> for Box<[T; N]> where diff --git a/src/test/ui/const-generics/array-impls/alloc-traits-impls-length-32.rs b/src/test/ui/const-generics/array-impls/alloc-traits-impls-length-32.rs index 0d0765e971d5..b4a083636b64 100644 --- a/src/test/ui/const-generics/array-impls/alloc-traits-impls-length-32.rs +++ b/src/test/ui/const-generics/array-impls/alloc-traits-impls-length-32.rs @@ -18,6 +18,10 @@ pub fn yes_array_into_vec() -> Vec { [].into() } +pub fn yes_array_into_box() -> Box<[T]> { + [].into() +} + use std::collections::VecDeque; pub fn yes_vecdeque_partial_eq_array() -> impl PartialEq<[B; 32]> diff --git a/src/test/ui/const-generics/array-impls/alloc-types-no-impls-length-33.rs b/src/test/ui/const-generics/array-impls/alloc-types-no-impls-length-33.rs index 4b195f3a06ed..48cf21d489ad 100644 --- a/src/test/ui/const-generics/array-impls/alloc-types-no-impls-length-33.rs +++ b/src/test/ui/const-generics/array-impls/alloc-types-no-impls-length-33.rs @@ -12,6 +12,8 @@ pub fn no_box() { let boxed_array = >::try_from(boxed_slice); //~^ ERROR the trait bound `std::boxed::Box<[i32; 33]>: std::convert::From>` is not satisfied //~^^ ERROR the trait bound `std::boxed::Box<[i32; 33]>: std::convert::TryFrom>` is not satisfied + let boxed_slice = >::from([0; 33]); + //~^ 15:42: 15:49: arrays only have std trait implementations for lengths 0..=32 [E0277] } pub fn no_rc() { diff --git a/src/test/ui/const-generics/array-impls/alloc-types-no-impls-length-33.stderr b/src/test/ui/const-generics/array-impls/alloc-types-no-impls-length-33.stderr index ce1c9ae551ea..5c01603ab881 100644 --- a/src/test/ui/const-generics/array-impls/alloc-types-no-impls-length-33.stderr +++ b/src/test/ui/const-generics/array-impls/alloc-types-no-impls-length-33.stderr @@ -18,10 +18,23 @@ LL | let boxed_array = >::try_from(boxed_slice); as std::convert::From<&str>> as std::convert::From>> as std::convert::From> - and 21 others + and 22 others = note: required because of the requirements on the impl of `std::convert::Into>` for `std::boxed::Box<[i32]>` = note: required because of the requirements on the impl of `std::convert::TryFrom>` for `std::boxed::Box<[i32; 33]>` +error[E0277]: arrays only have std trait implementations for lengths 0..=32 + --> $DIR/alloc-types-no-impls-length-33.rs:15:42 + | +LL | let boxed_slice = >::from([0; 33]); + | ^^^^^^^ + | | + | expected an implementor of trait `std::convert::From<[{integer}; 33]>` + | help: consider borrowing here: `&[0; 33]` + | + = note: the trait bound `[i32; 33]: std::convert::From<[{integer}; 33]>` is not satisfied + = note: required because of the requirements on the impl of `std::convert::From<[i32; 33]>` for `std::boxed::Box<[i32]>` + = note: required by `std::convert::From::from` + error[E0277]: the trait bound `std::boxed::Box<[i32; 33]>: std::convert::TryFrom>` is not satisfied --> $DIR/alloc-types-no-impls-length-33.rs:12:23 | @@ -32,7 +45,7 @@ LL | let boxed_array = >::try_from(boxed_slice); as std::convert::TryFrom>> error[E0277]: the trait bound `std::rc::Rc<[i32; 33]>: std::convert::From>` is not satisfied - --> $DIR/alloc-types-no-impls-length-33.rs:19:23 + --> $DIR/alloc-types-no-impls-length-33.rs:21:23 | LL | let boxed_array = >::try_from(boxed_slice); | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::From>` is not implemented for `std::rc::Rc<[i32; 33]>` @@ -47,7 +60,7 @@ LL | let boxed_array = >::try_from(boxed_slice); = note: required because of the requirements on the impl of `std::convert::TryFrom>` for `std::rc::Rc<[i32; 33]>` error[E0277]: the trait bound `std::rc::Rc<[i32; 33]>: std::convert::TryFrom>` is not satisfied - --> $DIR/alloc-types-no-impls-length-33.rs:19:23 + --> $DIR/alloc-types-no-impls-length-33.rs:21:23 | LL | let boxed_array = >::try_from(boxed_slice); | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::TryFrom>` is not implemented for `std::rc::Rc<[i32; 33]>` @@ -56,7 +69,7 @@ LL | let boxed_array = >::try_from(boxed_slice); as std::convert::TryFrom>> error[E0277]: the trait bound `std::sync::Arc<[i32; 33]>: std::convert::From>` is not satisfied - --> $DIR/alloc-types-no-impls-length-33.rs:26:23 + --> $DIR/alloc-types-no-impls-length-33.rs:28:23 | LL | let boxed_array = >::try_from(boxed_slice); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::From>` is not implemented for `std::sync::Arc<[i32; 33]>` @@ -71,7 +84,7 @@ LL | let boxed_array = >::try_from(boxed_slice); = note: required because of the requirements on the impl of `std::convert::TryFrom>` for `std::sync::Arc<[i32; 33]>` error[E0277]: the trait bound `std::sync::Arc<[i32; 33]>: std::convert::TryFrom>` is not satisfied - --> $DIR/alloc-types-no-impls-length-33.rs:26:23 + --> $DIR/alloc-types-no-impls-length-33.rs:28:23 | LL | let boxed_array = >::try_from(boxed_slice); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::TryFrom>` is not implemented for `std::sync::Arc<[i32; 33]>` @@ -79,6 +92,6 @@ LL | let boxed_array = >::try_from(boxed_slice); = help: the following implementations were found: as std::convert::TryFrom>> -error: aborting due to 7 previous errors +error: aborting due to 8 previous errors For more information about this error, try `rustc --explain E0277`. From eccaa0186f8fa2a432bf612b74f18a319406134c Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 2 May 2020 01:59:35 +0300 Subject: [PATCH 03/56] rustc_target: Add a target spec option for static-pie support --- src/librustc_codegen_ssa/back/link.rs | 7 ++++--- src/librustc_target/spec/mod.rs | 5 +++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/librustc_codegen_ssa/back/link.rs b/src/librustc_codegen_ssa/back/link.rs index d87b84f880a3..288a4a41e07f 100644 --- a/src/librustc_codegen_ssa/back/link.rs +++ b/src/librustc_codegen_ssa/back/link.rs @@ -1143,9 +1143,10 @@ fn link_output_kind(sess: &Session, crate_type: CrateType) -> LinkOutputKind { }; // Adjust the output kind to target capabilities. - let pic_exe_supported = sess.target.target.options.position_independent_executables; - let static_pic_exe_supported = false; // FIXME: Add this option to target specs. - let static_dylib_supported = sess.target.target.options.crt_static_allows_dylibs; + let opts = &sess.target.target.options; + let pic_exe_supported = opts.position_independent_executables; + let static_pic_exe_supported = opts.static_position_independent_executables; + let static_dylib_supported = opts.crt_static_allows_dylibs; match kind { LinkOutputKind::DynamicPicExe if !pic_exe_supported => LinkOutputKind::DynamicNoPicExe, LinkOutputKind::StaticPicExe if !static_pic_exe_supported => LinkOutputKind::StaticNoPicExe, diff --git a/src/librustc_target/spec/mod.rs b/src/librustc_target/spec/mod.rs index 41c2f1d93d2c..d92f5cdeab5a 100644 --- a/src/librustc_target/spec/mod.rs +++ b/src/librustc_target/spec/mod.rs @@ -855,6 +855,8 @@ pub struct TargetOptions { /// the functions in the executable are not randomized and can be used /// during an exploit of a vulnerability in any code. pub position_independent_executables: bool, + /// Executables that are both statically linked and position-independent are supported. + pub static_position_independent_executables: bool, /// Determines if the target always requires using the PLT for indirect /// library calls or not. This controls the default value of the `-Z plt` flag. pub needs_plt: bool, @@ -1028,6 +1030,7 @@ impl Default for TargetOptions { has_rpath: false, no_default_libraries: true, position_independent_executables: false, + static_position_independent_executables: false, needs_plt: false, relro_level: RelroLevel::None, pre_link_objects: Default::default(), @@ -1432,6 +1435,7 @@ impl Target { key!(has_rpath, bool); key!(no_default_libraries, bool); key!(position_independent_executables, bool); + key!(static_position_independent_executables, bool); key!(needs_plt, bool); key!(relro_level, RelroLevel)?; key!(archive_format); @@ -1663,6 +1667,7 @@ impl ToJson for Target { target_option_val!(has_rpath); target_option_val!(no_default_libraries); target_option_val!(position_independent_executables); + target_option_val!(static_position_independent_executables); target_option_val!(needs_plt); target_option_val!(relro_level); target_option_val!(archive_format); From 96a466c3128945627c2f81ec13b8ae98be7c3749 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 2 May 2020 02:22:48 +0300 Subject: [PATCH 04/56] linker: Support `-static-pie` and `-static -shared` --- src/librustc_codegen_ssa/back/link.rs | 22 +-- src/librustc_codegen_ssa/back/linker.rs | 227 ++++++++++++------------ 2 files changed, 118 insertions(+), 131 deletions(-) diff --git a/src/librustc_codegen_ssa/back/link.rs b/src/librustc_codegen_ssa/back/link.rs index 288a4a41e07f..7addfd15f1cd 100644 --- a/src/librustc_codegen_ssa/back/link.rs +++ b/src/librustc_codegen_ssa/back/link.rs @@ -1530,16 +1530,7 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( } // NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER - // FIXME: Support `StaticPicExe` correctly. - match link_output_kind { - LinkOutputKind::DynamicPicExe | LinkOutputKind::StaticPicExe => { - cmd.position_independent_executable() - } - LinkOutputKind::DynamicNoPicExe | LinkOutputKind::StaticNoPicExe => { - cmd.no_position_independent_executable() - } - _ => {} - } + cmd.set_output_kind(link_output_kind, out_filename); // OBJECT-FILES-NO, AUDIT-ORDER add_relro_args(cmd, sess); @@ -1568,17 +1559,6 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( tmpdir, ); - // NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER - // FIXME: Merge with the previous `link_output_kind` match, - // and support `StaticPicExe` and `StaticDylib` correctly. - match link_output_kind { - LinkOutputKind::StaticNoPicExe | LinkOutputKind::StaticPicExe => { - cmd.build_static_executable() - } - LinkOutputKind::DynamicDylib | LinkOutputKind::StaticDylib => cmd.build_dylib(out_filename), - _ => {} - } - // OBJECT-FILES-NO, AUDIT-ORDER if sess.opts.cg.profile_generate.enabled() { cmd.pgo_gen(); diff --git a/src/librustc_codegen_ssa/back/linker.rs b/src/librustc_codegen_ssa/back/linker.rs index ee5bcf4b9f58..80d1f7006847 100644 --- a/src/librustc_codegen_ssa/back/linker.rs +++ b/src/librustc_codegen_ssa/back/linker.rs @@ -17,7 +17,7 @@ use rustc_serialize::{json, Encoder}; use rustc_session::config::{self, CrateType, DebugInfo, LinkerPluginLto, Lto, OptLevel, Strip}; use rustc_session::Session; use rustc_span::symbol::Symbol; -use rustc_target::spec::{LinkerFlavor, LldFlavor}; +use rustc_target::spec::{LinkOutputKind, LinkerFlavor, LldFlavor}; /// Disables non-English messages from localized linkers. /// Such messages may cause issues with text encoding on Windows (#35785) @@ -102,6 +102,7 @@ impl LinkerInfo { /// MSVC linker (e.g., `link.exe`) is being used. pub trait Linker { fn cmd(&mut self) -> &mut Command; + fn set_output_kind(&mut self, output_kind: LinkOutputKind, out_filename: &Path); fn link_dylib(&mut self, lib: Symbol); fn link_rust_dylib(&mut self, lib: Symbol, path: &Path); fn link_framework(&mut self, framework: Symbol); @@ -114,8 +115,6 @@ pub trait Linker { fn output_filename(&mut self, path: &Path); fn add_object(&mut self, path: &Path); fn gc_sections(&mut self, keep_metadata: bool); - fn position_independent_executable(&mut self); - fn no_position_independent_executable(&mut self); fn full_relro(&mut self); fn partial_relro(&mut self); fn no_relro(&mut self); @@ -125,8 +124,6 @@ pub trait Linker { fn debuginfo(&mut self, strip: Strip); fn no_crt_objects(&mut self); fn no_default_libraries(&mut self); - fn build_dylib(&mut self, out_filename: &Path); - fn build_static_executable(&mut self); fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType); fn subsystem(&mut self, subsystem: &str); fn group_start(&mut self); @@ -233,12 +230,94 @@ impl<'a> GccLinker<'a> { let target_cpu = self.target_cpu; self.linker_arg(&format!("-plugin-opt=mcpu={}", target_cpu)); } + + fn build_dylib(&mut self, out_filename: &Path) { + // On mac we need to tell the linker to let this library be rpathed + if self.sess.target.target.options.is_like_osx { + self.cmd.arg("-dynamiclib"); + self.linker_arg("-dylib"); + + // Note that the `osx_rpath_install_name` option here is a hack + // purely to support rustbuild right now, we should get a more + // principled solution at some point to force the compiler to pass + // the right `-Wl,-install_name` with an `@rpath` in it. + if self.sess.opts.cg.rpath || self.sess.opts.debugging_opts.osx_rpath_install_name { + self.linker_arg("-install_name"); + let mut v = OsString::from("@rpath/"); + v.push(out_filename.file_name().unwrap()); + self.linker_arg(&v); + } + } else { + self.cmd.arg("-shared"); + if self.sess.target.target.options.is_like_windows { + // The output filename already contains `dll_suffix` so + // the resulting import library will have a name in the + // form of libfoo.dll.a + let implib_name = + out_filename.file_name().and_then(|file| file.to_str()).map(|file| { + format!( + "{}{}{}", + self.sess.target.target.options.staticlib_prefix, + file, + self.sess.target.target.options.staticlib_suffix + ) + }); + if let Some(implib_name) = implib_name { + let implib = out_filename.parent().map(|dir| dir.join(&implib_name)); + if let Some(implib) = implib { + self.linker_arg(&format!("--out-implib,{}", (*implib).to_str().unwrap())); + } + } + } + } + } } impl<'a> Linker for GccLinker<'a> { fn cmd(&mut self) -> &mut Command { &mut self.cmd } + + fn set_output_kind(&mut self, output_kind: LinkOutputKind, out_filename: &Path) { + match output_kind { + LinkOutputKind::DynamicNoPicExe => { + if !self.is_ld { + self.cmd.arg("-no-pie"); + } + } + LinkOutputKind::DynamicPicExe => { + // `-pie` works for both gcc wrapper and ld. + self.cmd.arg("-pie"); + } + LinkOutputKind::StaticNoPicExe => { + // `-static` works for both gcc wrapper and ld. + self.cmd.arg("-static"); + if !self.is_ld { + self.cmd.arg("-no-pie"); + } + } + LinkOutputKind::StaticPicExe => { + if !self.is_ld { + // Note that combination `-static -pie` doesn't work as expected + // for the gcc wrapper, `-static` in that case suppresses `-pie`. + self.cmd.arg("-static-pie"); + } else { + // `--no-dynamic-linker` and `-z text` are not strictly necessary for producing + // a static pie, but currently passed because gcc and clang pass them. + // The former suppresses the `INTERP` ELF header specifying dynamic linker, + // which is otherwise implicitly injected by ld (but not lld). + // The latter doesn't change anything, only ensures that everything is pic. + self.cmd.args(&["-static", "-pie", "--no-dynamic-linker", "-z", "text"]); + } + } + LinkOutputKind::DynamicDylib => self.build_dylib(out_filename), + LinkOutputKind::StaticDylib => { + self.cmd.arg("-static"); + self.build_dylib(out_filename); + } + } + } + fn link_dylib(&mut self, lib: Symbol) { self.hint_dynamic(); self.cmd.arg(format!("-l{}", lib)); @@ -263,14 +342,6 @@ impl<'a> Linker for GccLinker<'a> { fn add_object(&mut self, path: &Path) { self.cmd.arg(path); } - fn position_independent_executable(&mut self) { - self.cmd.arg("-pie"); - } - fn no_position_independent_executable(&mut self) { - if !self.is_ld { - self.cmd.arg("-no-pie"); - } - } fn full_relro(&mut self) { self.linker_arg("-zrelro"); self.linker_arg("-znow"); @@ -281,9 +352,6 @@ impl<'a> Linker for GccLinker<'a> { fn no_relro(&mut self) { self.linker_arg("-znorelro"); } - fn build_static_executable(&mut self) { - self.cmd.arg("-static"); - } fn link_rust_dylib(&mut self, lib: Symbol, _path: &Path) { self.hint_dynamic(); @@ -419,47 +487,6 @@ impl<'a> Linker for GccLinker<'a> { } } - fn build_dylib(&mut self, out_filename: &Path) { - // On mac we need to tell the linker to let this library be rpathed - if self.sess.target.target.options.is_like_osx { - self.cmd.arg("-dynamiclib"); - self.linker_arg("-dylib"); - - // Note that the `osx_rpath_install_name` option here is a hack - // purely to support rustbuild right now, we should get a more - // principled solution at some point to force the compiler to pass - // the right `-Wl,-install_name` with an `@rpath` in it. - if self.sess.opts.cg.rpath || self.sess.opts.debugging_opts.osx_rpath_install_name { - self.linker_arg("-install_name"); - let mut v = OsString::from("@rpath/"); - v.push(out_filename.file_name().unwrap()); - self.linker_arg(&v); - } - } else { - self.cmd.arg("-shared"); - if self.sess.target.target.options.is_like_windows { - // The output filename already contains `dll_suffix` so - // the resulting import library will have a name in the - // form of libfoo.dll.a - let implib_name = - out_filename.file_name().and_then(|file| file.to_str()).map(|file| { - format!( - "{}{}{}", - self.sess.target.target.options.staticlib_prefix, - file, - self.sess.target.target.options.staticlib_suffix - ) - }); - if let Some(implib_name) = implib_name { - let implib = out_filename.parent().map(|dir| dir.join(&implib_name)); - if let Some(implib) = implib { - self.linker_arg(&format!("--out-implib,{}", (*implib).to_str().unwrap())); - } - } - } - } - } - fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType) { // Symbol visibility in object files typically takes care of this. if crate_type == CrateType::Executable @@ -583,6 +610,22 @@ impl<'a> Linker for MsvcLinker<'a> { fn cmd(&mut self) -> &mut Command { &mut self.cmd } + + fn set_output_kind(&mut self, output_kind: LinkOutputKind, out_filename: &Path) { + match output_kind { + LinkOutputKind::DynamicNoPicExe + | LinkOutputKind::DynamicPicExe + | LinkOutputKind::StaticNoPicExe + | LinkOutputKind::StaticPicExe => {} + LinkOutputKind::DynamicDylib | LinkOutputKind::StaticDylib => { + self.cmd.arg("/DLL"); + let mut arg: OsString = "/IMPLIB:".into(); + arg.push(out_filename.with_extension("dll.lib")); + self.cmd.arg(arg); + } + } + } + fn link_rlib(&mut self, lib: &Path) { self.cmd.arg(lib); } @@ -590,17 +633,6 @@ impl<'a> Linker for MsvcLinker<'a> { self.cmd.arg(path); } - fn build_dylib(&mut self, out_filename: &Path) { - self.cmd.arg("/DLL"); - let mut arg: OsString = "/IMPLIB:".into(); - arg.push(out_filename.with_extension("dll.lib")); - self.cmd.arg(arg); - } - - fn build_static_executable(&mut self) { - // noop - } - fn gc_sections(&mut self, _keep_metadata: bool) { // MSVC's ICF (Identical COMDAT Folding) link optimization is // slow for Rust and thus we disable it by default when not in @@ -633,14 +665,6 @@ impl<'a> Linker for MsvcLinker<'a> { self.cmd.arg(&format!("{}.lib", lib)); } - fn position_independent_executable(&mut self) { - // noop - } - - fn no_position_independent_executable(&mut self) { - // noop - } - fn full_relro(&mut self) { // noop } @@ -818,6 +842,9 @@ impl<'a> Linker for EmLinker<'a> { fn cmd(&mut self) -> &mut Command { &mut self.cmd } + + fn set_output_kind(&mut self, _output_kind: LinkOutputKind, _out_filename: &Path) {} + fn include_path(&mut self, path: &Path) { self.cmd.arg("-L").arg(path); } @@ -857,14 +884,6 @@ impl<'a> Linker for EmLinker<'a> { self.add_object(lib); } - fn position_independent_executable(&mut self) { - // noop - } - - fn no_position_independent_executable(&mut self) { - // noop - } - fn full_relro(&mut self) { // noop } @@ -926,14 +945,6 @@ impl<'a> Linker for EmLinker<'a> { self.cmd.args(&["-s", "DEFAULT_LIBRARY_FUNCS_TO_INCLUDE=[]"]); } - fn build_dylib(&mut self, _out_filename: &Path) { - bug!("building dynamic library is unsupported on Emscripten") - } - - fn build_static_executable(&mut self) { - // noop - } - fn export_symbols(&mut self, _tmpdir: &Path, crate_type: CrateType) { let symbols = &self.info.exports[&crate_type]; @@ -1032,6 +1043,18 @@ impl<'a> Linker for WasmLd<'a> { &mut self.cmd } + fn set_output_kind(&mut self, output_kind: LinkOutputKind, _out_filename: &Path) { + match output_kind { + LinkOutputKind::DynamicNoPicExe + | LinkOutputKind::DynamicPicExe + | LinkOutputKind::StaticNoPicExe + | LinkOutputKind::StaticPicExe => {} + LinkOutputKind::DynamicDylib | LinkOutputKind::StaticDylib => { + self.cmd.arg("--no-entry"); + } + } + } + fn link_dylib(&mut self, lib: Symbol) { self.cmd.arg("-l").sym_arg(lib); } @@ -1060,16 +1083,12 @@ impl<'a> Linker for WasmLd<'a> { self.cmd.arg(path); } - fn position_independent_executable(&mut self) {} - fn full_relro(&mut self) {} fn partial_relro(&mut self) {} fn no_relro(&mut self) {} - fn build_static_executable(&mut self) {} - fn link_rust_dylib(&mut self, lib: Symbol, _path: &Path) { self.cmd.arg("-l").sym_arg(lib); } @@ -1125,10 +1144,6 @@ impl<'a> Linker for WasmLd<'a> { fn no_default_libraries(&mut self) {} - fn build_dylib(&mut self, _out_filename: &Path) { - self.cmd.arg("--no-entry"); - } - fn export_symbols(&mut self, _tmpdir: &Path, crate_type: CrateType) { for sym in self.info.exports[&crate_type].iter() { self.cmd.arg("--export").arg(&sym); @@ -1144,8 +1159,6 @@ impl<'a> Linker for WasmLd<'a> { fn subsystem(&mut self, _subsystem: &str) {} - fn no_position_independent_executable(&mut self) {} - fn finalize(&mut self) {} // Not needed for now with LLD @@ -1208,6 +1221,8 @@ impl<'a> Linker for PtxLinker<'a> { &mut self.cmd } + fn set_output_kind(&mut self, _output_kind: LinkOutputKind, _out_filename: &Path) {} + fn link_rlib(&mut self, path: &Path) { self.cmd.arg("--rlib").arg(path); } @@ -1274,16 +1289,12 @@ impl<'a> Linker for PtxLinker<'a> { panic!("frameworks not supported") } - fn position_independent_executable(&mut self) {} - fn full_relro(&mut self) {} fn partial_relro(&mut self) {} fn no_relro(&mut self) {} - fn build_static_executable(&mut self) {} - fn gc_sections(&mut self, _keep_metadata: bool) {} fn pgo_gen(&mut self) {} @@ -1296,14 +1307,10 @@ impl<'a> Linker for PtxLinker<'a> { self.sess.warn("Windows Control Flow Guard is not supported by this linker."); } - fn build_dylib(&mut self, _out_filename: &Path) {} - fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType) {} fn subsystem(&mut self, _subsystem: &str) {} - fn no_position_independent_executable(&mut self) {} - fn group_start(&mut self) {} fn group_end(&mut self) {} From 44ca3da9a386665834e34d27e30ed471253f2777 Mon Sep 17 00:00:00 2001 From: Nathan West Date: Wed, 20 May 2020 21:10:02 -0400 Subject: [PATCH 05/56] Various minor improvements to Ipv6Addr::Display - Defer to Ipv4Addr::fmt when printing an Ipv4 address - Fast path: write directly to f without an intermediary buffer when there are no alignment options - Simplify finding the inner zeroes-span --- src/libstd/net/ip.rs | 158 +++++++++++++++++++++---------------------- 1 file changed, 78 insertions(+), 80 deletions(-) diff --git a/src/libstd/net/ip.rs b/src/libstd/net/ip.rs index edc28033c9b8..d50230a8859a 100644 --- a/src/libstd/net/ip.rs +++ b/src/libstd/net/ip.rs @@ -7,9 +7,9 @@ )] use crate::cmp::Ordering; -use crate::fmt; +use crate::fmt::{self, Write as FmtWrite}; use crate::hash; -use crate::io::Write; +use crate::io::Write as IoWrite; use crate::sys::net::netc as c; use crate::sys_common::{AsInner, FromInner}; @@ -1525,102 +1525,100 @@ impl Ipv6Addr { } } +/// Write an Ipv6Addr, conforming to the canonical style described by +/// [RFC 5952](https://tools.ietf.org/html/rfc5952). #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Display for Ipv6Addr { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - // Note: The calls to write should never fail, hence the unwraps in the function - // Long enough for the longest possible IPv6: 39 - const IPV6_BUF_LEN: usize = 39; - let mut buf = [0u8; IPV6_BUF_LEN]; - let mut buf_slice = &mut buf[..]; + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // If there are no alignment requirements, write out the IP address to + // f. Otherwise, write it to a local buffer, then use f.pad. + if f.precision().is_none() && f.width().is_none() { + let segments = self.segments(); + + // Special case for :: and ::1; otherwise they get written with the + // IPv4 formatter + if self.is_unspecified() { + f.write_str("::") + } else if self.is_loopback() { + f.write_str("::1") + } else if let Some(ipv4) = self.to_ipv4() { + match segments[5] { + // IPv4 Compatible address + 0 => write!(f, "::{}", ipv4), + // IPv4 Mapped address + 0xffff => write!(f, "::ffff:{}", ipv4), + _ => unreachable!(), + } + } else { + #[derive(Copy, Clone, Default)] + struct Span { + start: usize, + len: usize, + } - match self.segments() { - // We need special cases for :: and ::1, otherwise they're formatted - // as ::0.0.0.[01] - [0, 0, 0, 0, 0, 0, 0, 0] => write!(buf_slice, "::").unwrap(), - [0, 0, 0, 0, 0, 0, 0, 1] => write!(buf_slice, "::1").unwrap(), - // Ipv4 Compatible address - [0, 0, 0, 0, 0, 0, g, h] => { - write!( - buf_slice, - "::{}.{}.{}.{}", - (g >> 8) as u8, - g as u8, - (h >> 8) as u8, - h as u8 - ) - .unwrap(); - } - // Ipv4-Mapped address - [0, 0, 0, 0, 0, 0xffff, g, h] => { - write!( - buf_slice, - "::ffff:{}.{}.{}.{}", - (g >> 8) as u8, - g as u8, - (h >> 8) as u8, - h as u8 - ) - .unwrap(); - } - _ => { - fn find_zero_slice(segments: &[u16; 8]) -> (usize, usize) { - let mut longest_span_len = 0; - let mut longest_span_at = 0; - let mut cur_span_len = 0; - let mut cur_span_at = 0; - - for i in 0..8 { - if segments[i] == 0 { - if cur_span_len == 0 { - cur_span_at = i; + // Find the inner 0 span + let zeroes = { + let mut longest = Span::default(); + let mut current = Span::default(); + + for (i, &segment) in segments.iter().enumerate() { + if segment == 0 { + if current.len == 0 { + current.start = i; } - cur_span_len += 1; + current.len += 1; - if cur_span_len > longest_span_len { - longest_span_len = cur_span_len; - longest_span_at = cur_span_at; + if current.len > longest.len { + longest = current; } } else { - cur_span_len = 0; - cur_span_at = 0; + current = Span::default(); } } - (longest_span_at, longest_span_len) - } - - let (zeros_at, zeros_len) = find_zero_slice(&self.segments()); - - if zeros_len > 1 { - fn fmt_subslice(segments: &[u16], buf: &mut &mut [u8]) { - if !segments.is_empty() { - write!(*buf, "{:x}", segments[0]).unwrap(); - for &seg in &segments[1..] { - write!(*buf, ":{:x}", seg).unwrap(); - } + longest + }; + + /// Write a colon-separated part of the address + #[inline] + fn fmt_subslice(f: &mut fmt::Formatter<'_>, chunk: &[u16]) -> fmt::Result { + if let Some(first) = chunk.first() { + fmt::LowerHex::fmt(first, f)?; + for segment in &chunk[1..] { + f.write_char(':')?; + fmt::LowerHex::fmt(segment, f)?; } } + Ok(()) + } - fmt_subslice(&self.segments()[..zeros_at], &mut buf_slice); - write!(buf_slice, "::").unwrap(); - fmt_subslice(&self.segments()[zeros_at + zeros_len..], &mut buf_slice); + if zeroes.len > 1 { + fmt_subslice(f, &segments[..zeroes.start])?; + f.write_str("::")?; + fmt_subslice(f, &segments[zeroes.start + zeroes.len..]) } else { - let &[a, b, c, d, e, f, g, h] = &self.segments(); - write!( - buf_slice, - "{:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x}", - a, b, c, d, e, f, g, h - ) - .unwrap(); + fmt_subslice(f, &segments) } } + } else { + // Slow path: write the address to a local buffer, the use f.pad. + // Defined recursively by using the fast path to write to the + // buffer. + + // This is the largest possible size of an IPv6 address + const IPV6_BUF_LEN: usize = (4 * 8) + 7; + let mut buf = [0u8; IPV6_BUF_LEN]; + let mut buf_slice = &mut buf[..]; + + // Note: This call to write should never fail, so unwrap is okay. + write!(buf_slice, "{}", self).unwrap(); + let len = IPV6_BUF_LEN - buf_slice.len(); + + // This is safe because we know exactly what can be in this buffer + let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) }; + f.pad(buf) } - let len = IPV6_BUF_LEN - buf_slice.len(); - // This is safe because we know exactly what can be in this buffer - let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) }; - fmt.pad(buf) } } From 96f387920c21a83fa620b1838066fd4c73c4b486 Mon Sep 17 00:00:00 2001 From: CAD97 Date: Wed, 20 May 2020 23:46:57 -0400 Subject: [PATCH 06/56] impl Step for char Enables Range to be iterable Note: https://rust.godbolt.org/z/fdveKo An iteration over all char ('\0'..=char::MAX) includes unreachable panic code currently. Updating RangeInclusive::next to call Step::forward_unchecked (which is safe to do but not done yet becuase it wasn't necessary) successfully removes the panic from this iteration. --- src/libcore/iter/range.rs | 64 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/src/libcore/iter/range.rs b/src/libcore/iter/range.rs index d74df82bddd9..6837aab7af1a 100644 --- a/src/libcore/iter/range.rs +++ b/src/libcore/iter/range.rs @@ -1,3 +1,4 @@ +use crate::char; use crate::convert::TryFrom; use crate::mem; use crate::ops::{self, Add, Sub, Try}; @@ -400,6 +401,69 @@ step_integer_impls! { wider than usize: [u32 i32], [u64 i64], [u128 i128]; } +#[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] +unsafe impl Step for char { + #[inline] + fn steps_between(&start: &char, &end: &char) -> Option { + let start = start as u32; + let end = end as u32; + if start <= end { + let count = end - start + 1; + if start < 0xD800 && 0xE000 <= end { + usize::try_from(count - 0x800).ok() + } else { + usize::try_from(count).ok() + } + } else { + None + } + } + + #[inline] + fn forward_checked(start: char, count: usize) -> Option { + let start = start as u32; + let mut res = Step::forward_checked(start, count)?; + if start < 0xD800 && 0xD800 <= res { + res = Step::forward_checked(res, 0x800)?; + } + if res <= char::MAX as u32 { + Some(unsafe { char::from_u32_unchecked(res) }) + } else { + None + } + } + + #[inline] + fn backward_checked(start: char, count: usize) -> Option { + let start = start as u32; + let mut res = Step::backward_checked(start, count)?; + if start >= 0xE000 && 0xE000 > res { + res = Step::backward_checked(res, 0x800)?; + } + Some(unsafe { char::from_u32_unchecked(res) }) + } + + #[inline] + unsafe fn forward_unchecked(start: char, count: usize) -> char { + let start = start as u32; + let mut res = Step::forward_unchecked(start, count); + if start < 0xD800 && 0xD800 <= res { + res = Step::forward_unchecked(res, 0x800); + } + char::from_u32_unchecked(res) + } + + #[inline] + unsafe fn backward_unchecked(start: char, count: usize) -> char { + let start = start as u32; + let mut res = Step::backward_unchecked(start, count); + if start >= 0xE000 && 0xE000 > res { + res = Step::backward_unchecked(res, 0x800); + } + char::from_u32_unchecked(res) + } +} + macro_rules! range_exact_iter_impl { ($($t:ty)*) => ($( #[stable(feature = "rust1", since = "1.0.0")] From c25b82f5bb9770374db31906dedb497034a151fd Mon Sep 17 00:00:00 2001 From: CAD97 Date: Wed, 20 May 2020 23:50:28 -0400 Subject: [PATCH 07/56] Use Step::forward_unchecked in RangeInclusive::next --- src/libcore/iter/range.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libcore/iter/range.rs b/src/libcore/iter/range.rs index 6837aab7af1a..3fa4cf2ca0da 100644 --- a/src/libcore/iter/range.rs +++ b/src/libcore/iter/range.rs @@ -646,7 +646,11 @@ impl Iterator for ops::RangeInclusive { } let is_iterating = self.start < self.end; Some(if is_iterating { - let n = Step::forward(self.start.clone(), 1); + // SAFETY: just checked precondition + // We use the unchecked version here, because + // otherwise `for _ in '\0'..=char::MAX` + // does not successfully remove panicking code. + let n = unsafe { Step::forward_unchecked(self.start.clone(), 1) }; mem::replace(&mut self.start, n) } else { self.exhausted = true; From 27d1cd857e8998c423f329efce7c995a26dcdb6c Mon Sep 17 00:00:00 2001 From: CAD97 Date: Thu, 21 May 2020 13:21:55 -0400 Subject: [PATCH 08/56] Add safety annotations in iter::range --- src/libcore/iter/range.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libcore/iter/range.rs b/src/libcore/iter/range.rs index 3fa4cf2ca0da..5db790e491c2 100644 --- a/src/libcore/iter/range.rs +++ b/src/libcore/iter/range.rs @@ -427,6 +427,8 @@ unsafe impl Step for char { res = Step::forward_checked(res, 0x800)?; } if res <= char::MAX as u32 { + // SAFETY: res is a valid unicode scalar + // (below 0x110000 and not in 0xD800..0xE000) Some(unsafe { char::from_u32_unchecked(res) }) } else { None @@ -440,6 +442,8 @@ unsafe impl Step for char { if start >= 0xE000 && 0xE000 > res { res = Step::backward_checked(res, 0x800)?; } + // SAFETY: res is a valid unicode scalar + // (below 0x110000 and not in 0xD800..0xE000) Some(unsafe { char::from_u32_unchecked(res) }) } From b1d1f256ef1902312803d3d1bd0235bfdf864aec Mon Sep 17 00:00:00 2001 From: CAD97 Date: Thu, 21 May 2020 13:43:01 -0400 Subject: [PATCH 09/56] Add a test for char ranges --- src/libcore/tests/iter.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/libcore/tests/iter.rs b/src/libcore/tests/iter.rs index 52cf068f0a56..6605249f1800 100644 --- a/src/libcore/tests/iter.rs +++ b/src/libcore/tests/iter.rs @@ -1932,6 +1932,16 @@ fn test_range() { ); } +#[test] +fn test_char_range() { + use std::char; + assert!(('\0'..=char::MAX).eq((0..=char::MAX as u32).filter_map(char::from_u32))); + assert!(('\0'..=char::MAX).rev().eq((0..=char::MAX as u32).filter_map(char::from_u32).rev())); + + assert_eq!(('\u{D7FF}'..='\u{E000}').count(), 2); + assert_eq!(('\u{D7FF}'..'\u{E000}').count(), 1); +} + #[test] fn test_range_exhaustion() { let mut r = 10..10; From 58fdc43e03bd77c575bc1c62b18b784f71ab4bf2 Mon Sep 17 00:00:00 2001 From: Michal Sudwoj Date: Thu, 21 May 2020 23:01:22 +0200 Subject: [PATCH 10/56] NVPTX support for new asm! --- src/librustc_codegen_llvm/asm.rs | 12 +++ src/librustc_target/asm/mod.rs | 27 +++++++ src/librustc_target/asm/nvptx.rs | 90 ++++++++++++++++++++++ src/test/assembly/asm/nvptx-types.rs | 109 +++++++++++++++++++++++++++ 4 files changed, 238 insertions(+) create mode 100644 src/librustc_target/asm/nvptx.rs create mode 100644 src/test/assembly/asm/nvptx-types.rs diff --git a/src/librustc_codegen_llvm/asm.rs b/src/librustc_codegen_llvm/asm.rs index 8986ab322c07..fe4cd16c1f5e 100644 --- a/src/librustc_codegen_llvm/asm.rs +++ b/src/librustc_codegen_llvm/asm.rs @@ -254,6 +254,7 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> { ]); } InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => {} + InlineAsmArch::Nvptx64 => {} } } if !options.contains(InlineAsmOptions::NOMEM) { @@ -410,6 +411,11 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass) -> String { | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low4) => "x", InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg) | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg) => "w", + InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg16) => "h", + InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg32) => "r", + InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg64) => "l", + InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::freg32) => "f", + InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::freg64) => "d", InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => "r", InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => "f", InlineAsmRegClass::X86(X86InlineAsmRegClass::reg) => "r", @@ -452,6 +458,7 @@ fn modifier_to_llvm( modifier } } + InlineAsmRegClass::Nvptx(_) => None, InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) | InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => None, InlineAsmRegClass::X86(X86InlineAsmRegClass::reg) @@ -502,6 +509,11 @@ fn dummy_output_type(cx: &CodegenCx<'ll, 'tcx>, reg: InlineAsmRegClass) -> &'ll | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low4) => { cx.type_vector(cx.type_i64(), 2) } + InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg16) => cx.type_i16(), + InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg32) => cx.type_i32(), + InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg64) => cx.type_i64(), + InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::freg32) => cx.type_f32(), + InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::freg64) => cx.type_f64(), InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => cx.type_i32(), InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => cx.type_f32(), InlineAsmRegClass::X86(X86InlineAsmRegClass::reg) diff --git a/src/librustc_target/asm/mod.rs b/src/librustc_target/asm/mod.rs index 774146a679ab..ffca742e9ab8 100644 --- a/src/librustc_target/asm/mod.rs +++ b/src/librustc_target/asm/mod.rs @@ -146,11 +146,13 @@ macro_rules! types { mod aarch64; mod arm; +mod nvptx; mod riscv; mod x86; pub use aarch64::{AArch64InlineAsmReg, AArch64InlineAsmRegClass}; pub use arm::{ArmInlineAsmReg, ArmInlineAsmRegClass}; +pub use nvptx::{NvptxInlineAsmReg, NvptxInlineAsmRegClass}; pub use riscv::{RiscVInlineAsmReg, RiscVInlineAsmRegClass}; pub use x86::{X86InlineAsmReg, X86InlineAsmRegClass}; @@ -162,6 +164,7 @@ pub enum InlineAsmArch { AArch64, RiscV32, RiscV64, + Nvptx64, } impl FromStr for InlineAsmArch { @@ -175,6 +178,7 @@ impl FromStr for InlineAsmArch { "aarch64" => Ok(Self::AArch64), "riscv32" => Ok(Self::RiscV32), "riscv64" => Ok(Self::RiscV64), + "nvptx64" => Ok(Self::Nvptx64), _ => Err(()), } } @@ -196,6 +200,7 @@ pub enum InlineAsmReg { Arm(ArmInlineAsmReg), AArch64(AArch64InlineAsmReg), RiscV(RiscVInlineAsmReg), + Nvptx(NvptxInlineAsmReg), } impl InlineAsmReg { @@ -205,6 +210,7 @@ impl InlineAsmReg { Self::Arm(r) => r.name(), Self::AArch64(r) => r.name(), Self::RiscV(r) => r.name(), + Self::Nvptx(r) => r.name(), } } @@ -214,6 +220,7 @@ impl InlineAsmReg { Self::Arm(r) => InlineAsmRegClass::Arm(r.reg_class()), Self::AArch64(r) => InlineAsmRegClass::AArch64(r.reg_class()), Self::RiscV(r) => InlineAsmRegClass::RiscV(r.reg_class()), + Self::Nvptx(r) => InlineAsmRegClass::Nvptx(r.reg_class()), } } @@ -236,6 +243,9 @@ impl InlineAsmReg { InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => { Self::RiscV(RiscVInlineAsmReg::parse(arch, has_feature, &name)?) } + InlineAsmArch::Nvptx64 => { + Self::Nvptx(NvptxInlineAsmReg::parse(arch, has_feature, &name)?) + } }) } @@ -252,6 +262,7 @@ impl InlineAsmReg { Self::Arm(r) => r.emit(out, arch, modifier), Self::AArch64(r) => r.emit(out, arch, modifier), Self::RiscV(r) => r.emit(out, arch, modifier), + Self::Nvptx(r) => r.emit(out, arch, modifier), } } @@ -261,6 +272,7 @@ impl InlineAsmReg { Self::Arm(r) => r.overlapping_regs(|r| cb(Self::Arm(r))), Self::AArch64(_) => cb(self), Self::RiscV(_) => cb(self), + Self::Nvptx(_) => cb(self), } } } @@ -281,6 +293,7 @@ pub enum InlineAsmRegClass { Arm(ArmInlineAsmRegClass), AArch64(AArch64InlineAsmRegClass), RiscV(RiscVInlineAsmRegClass), + Nvptx(NvptxInlineAsmRegClass), } impl InlineAsmRegClass { @@ -290,6 +303,7 @@ impl InlineAsmRegClass { Self::Arm(r) => r.name(), Self::AArch64(r) => r.name(), Self::RiscV(r) => r.name(), + Self::Nvptx(r) => r.name(), } } @@ -302,6 +316,7 @@ impl InlineAsmRegClass { Self::Arm(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Arm), Self::AArch64(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::AArch64), Self::RiscV(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::RiscV), + Self::Nvptx(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Nvptx), } } @@ -321,6 +336,7 @@ impl InlineAsmRegClass { Self::Arm(r) => r.suggest_modifier(arch, ty), Self::AArch64(r) => r.suggest_modifier(arch, ty), Self::RiscV(r) => r.suggest_modifier(arch, ty), + Self::Nvptx(r) => r.suggest_modifier(arch, ty), } } @@ -336,6 +352,7 @@ impl InlineAsmRegClass { Self::Arm(r) => r.default_modifier(arch), Self::AArch64(r) => r.default_modifier(arch), Self::RiscV(r) => r.default_modifier(arch), + Self::Nvptx(r) => r.default_modifier(arch), } } @@ -350,6 +367,7 @@ impl InlineAsmRegClass { Self::Arm(r) => r.supported_types(arch), Self::AArch64(r) => r.supported_types(arch), Self::RiscV(r) => r.supported_types(arch), + Self::Nvptx(r) => r.supported_types(arch), } } @@ -367,6 +385,9 @@ impl InlineAsmRegClass { InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => { Self::RiscV(RiscVInlineAsmRegClass::parse(arch, name)?) } + InlineAsmArch::Nvptx64 => { + Self::Nvptx(NvptxInlineAsmRegClass::parse(arch, name)?) + } }) }) } @@ -379,6 +400,7 @@ impl InlineAsmRegClass { Self::Arm(r) => r.valid_modifiers(arch), Self::AArch64(r) => r.valid_modifiers(arch), Self::RiscV(r) => r.valid_modifiers(arch), + Self::Nvptx(r) => r.valid_modifiers(arch), } } } @@ -518,5 +540,10 @@ pub fn allocatable_registers( riscv::fill_reg_map(arch, has_feature, &mut map); map } + InlineAsmArch::Nvptx64 => { + let mut map = nvptx::regclass_map(); + nvptx::fill_reg_map(arch, has_feature, &mut map); + map + } } } diff --git a/src/librustc_target/asm/nvptx.rs b/src/librustc_target/asm/nvptx.rs new file mode 100644 index 000000000000..b574e921718a --- /dev/null +++ b/src/librustc_target/asm/nvptx.rs @@ -0,0 +1,90 @@ +use super::{InlineAsmArch, InlineAsmType}; +use rustc_macros::HashStable_Generic; +use std::fmt; + +def_reg_class! { + Nvptx NvptxInlineAsmRegClass { + reg16, + reg32, + reg64, + freg32, + freg64, + } +} + +impl NvptxInlineAsmRegClass { + pub fn valid_modifiers(self, _arch: InlineAsmArch) -> &'static [char] { + &[] + } + + pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option { + None + } + + pub fn suggest_modifier( + self, + _arch: InlineAsmArch, + _ty: InlineAsmType, + ) -> Option<(char, &'static str)> { + None + } + + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + None + } + + pub fn supported_types( + self, + _arch: InlineAsmArch, + ) -> &'static [(InlineAsmType, Option<&'static str>)] { + match self { + Self::reg16 => types! { _: I8, I16; }, + Self::reg32 => types! { _: I8, I16, I32; }, + Self::reg64 => types! { _: I8, I16, I32, I64; }, + Self::freg32 => types! { _: F32; }, + Self::freg64 => types! { _: F32, F64; }, + } + } +} + +def_regs! { + Nvptx NvptxInlineAsmReg NvptxInlineAsmRegClass { + // We have to define a register, otherwise we get warnings/errors about unused imports and + // unreachable code. Do what clang does and define r0. + r0: reg32 = ["r0"], + #error = ["tid", "tid.x", "tid.y", "tid.z"] => "tid not supported for inline asm", + #error = ["ntid", "ntid.x", "ntid.y", "ntid.z"] => "ntid not supported for inline asm", + #error = ["laneid"] => "laneid not supported for inline asm", + #error = ["warpid"] => "warpid not supported for inline asm", + #error = ["nwarpid"] => "nwarpid not supported for inline asm", + #error = ["ctaid", "ctaid.x", "ctaid.y", "ctaid.z"] => "ctaid not supported for inline asm", + #error = ["nctaid", "nctaid.x", "nctaid.y", "nctaid.z"] => "nctaid not supported for inline asm", + #error = ["smid"] => "smid not supported for inline asm", + #error = ["nsmid"] => "nsmid not supported for inline asm", + #error = ["gridid"] => "gridid not supported for inline asm", + #error = ["lanemask_eq"] => "lanemask_eq not supported for inline asm", + #error = ["lanemask_le"] => "lanemask_le not supported for inline asm", + #error = ["lanemask_lt"] => "lanemask_lt not supported for inline asm", + #error = ["lanemask_ge"] => "lanemask_ge not supported for inline asm", + #error = ["lanemask_gt"] => "lanemask_gt not supported for inline asm", + #error = ["clock", "clock_hi"] => "clock not supported for inline asm", + #error = ["clock64"] => "clock64 not supported for inline asm", + #error = ["pm0", "pm1", "pm2", "pm3", "pm4", "pm5", "pm6", "pm7"] => "pm not supported for inline asm", + #error = ["pm0_64", "pm1_64", "pm2_64", "pm3_64", "pm4_64", "pm5_64", "pm6_64", "pm7_64"] => "pm_64 not supported for inline asm", + #error = ["envreg0", "envreg1", "envreg2", "envreg3", "envreg4", "envreg5", "envreg6", "envreg7", "envreg8", "envreg9", "envreg10", "envreg11", "envreg12", "envreg13", "envreg14", "envreg15", "envreg16", "envreg17", "envreg18", "envreg19", "envreg20", "envreg21", "envreg22", "envreg23", "envreg24", "envreg25", "envreg26", "envreg27", "envreg28", "envreg29", "envreg30", "envreg31"] => "envreg not supported for inline asm", + #error = ["globaltimer", "globaltimer_lo", "globaltimer_hi"] => "globaltimer not supported for inline asm", + #error = ["total_mem_size"] => "total_mem_size not supported for inline asm", + #error = ["dynamic_mem_size"] => "dynamic_mem_size not supported for inline asm", + } +} + +impl NvptxInlineAsmReg { + pub fn emit( + self, + out: &mut dyn fmt::Write, + _arch: InlineAsmArch, + _modifier: Option, + ) -> fmt::Result { + out.write_str(self.name()) + } +} diff --git a/src/test/assembly/asm/nvptx-types.rs b/src/test/assembly/asm/nvptx-types.rs new file mode 100644 index 000000000000..cfaab2351245 --- /dev/null +++ b/src/test/assembly/asm/nvptx-types.rs @@ -0,0 +1,109 @@ +// no-system-llvm +// assembly-output: emit-asm +// compile-flags: --target --nvptx64-nvidia-cuda +// only-nvptx64 +// ignore-nvptx64 + +#![feature(no_core, lang_items, rustc_attrs)] +#![crate_type = "rlib"] +#![no_core] +#![allow(asm_sub_register, non_camel_case_types)] + +#[rustc_builtin_macro] +macro_rules! asm { + () => {}; +} +#[rustc_builtin_macro] +macro_rules! concat { + () => {}; +} +#[rustc_builtin_macro] +macro_rules! stringify { + () => {}; +} + +#[lang = "sized"] +trait Sized {} +#[lang = "copy"] +trait Copy {} + +type ptr = *mut u8; + +impl Copy for i8 {} +impl Copy for i16 {} +impl Copy for i32 {} +impl Copy for f32 {} +impl Copy for i64 {} +impl Copy for f64 {} +impl Copy for ptr {} + +#[no_mangle] +fn extern_func(); + +// CHECK-LABEL: sym_fn +// CHECK: #APP +// CHECK call extern_func; +// CHECK: #NO_APP +#[no_mangle] +pub unsafe fn sym_fn() { + asm!("call {}", sym extern_func); +} + +macro_rules! check { + ($func:ident $ty:ident, $class:ident $mov:literal) => { + #[no_mangle] + pub unsafe fn $func(x: $ty) -> $ty { + // Hack to avoid function merging + extern "Rust" { + fn dont_merge(s: &str); + } + dont_merge(stringify!($func)); + + let y; + asm!(concat!($mov, " {}, {};"), out($class) y, in($class) x); + y + } + }; +} + +// CHECK-LABEL: reg_i8 +// CHECK: #APP +// CHECK: mov.i16 {{[a-z0-9]+}}, {{[a-z0-9]+}}; +// CHECK: #NO_APP +check!(reg_i8 i8 reg16 "mov.i16"); + +// CHECK-LABEL: reg_i16 +// CHECK: #APP +// CHECK: mov.i16 {{[a-z0-9]+}}, {{[a-z0-9]+}}; +// CHECK: #NO_APP +check!(reg_i16 i16 reg16 "mov.i16"); + +// CHECK-LABEL: reg_i32 +// CHECK: #APP +// CHECK: mov.i32 {{[a-z0-9]+}}, {{[a-z0-9]+}}; +// CHECK: #NO_APP +check!(reg_i32 i32 reg32 "mov.i32"); + +// CHECK-LABEL: reg_f32 +// CHECK: #APP +// CHECK: mov.f32 {{[a-z0-9]+}}, {{[a-z0-9]+}}; +// CHECK: #NO_APP +check!(reg_f32 f32 freg32 "mov.f32"); + +// CHECK-LABEL: reg_i54 +// CHECK: #APP +// CHECK: mov.i64 {{[a-z0-9]+}}, {{[a-z0-9]+}}; +// CHECK: #NO_APP +check!(reg_i64 i64 reg64 "mov.i64"); + +// CHECK-LABEL: reg_f64 +// CHECK: #APP +// CHECK: mov.f64 {{[a-z0-9]+}}, {{[a-z0-9]+}}; +// CHECK: #NO_APP +check!(reg_f64 f64 freg64 "mov.f64"); + +// CHECK-LABEL: reg_ptr +// CHECK: #APP +// CHECK: mov.i64 {{[a-z0-9]+}}, {{[a-z0-9]+}}; +// CHECK: #NO_APP +check!(reg_ptr ptr reg64 "mov.i64"); From d77f73e8818453afd513f96e98cf7e0c889cdbd9 Mon Sep 17 00:00:00 2001 From: Michal Sudwoj Date: Fri, 22 May 2020 00:31:50 +0200 Subject: [PATCH 11/56] Formatted correctly --- src/librustc_target/asm/mod.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/librustc_target/asm/mod.rs b/src/librustc_target/asm/mod.rs index ffca742e9ab8..99061d1fdeb8 100644 --- a/src/librustc_target/asm/mod.rs +++ b/src/librustc_target/asm/mod.rs @@ -385,9 +385,7 @@ impl InlineAsmRegClass { InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => { Self::RiscV(RiscVInlineAsmRegClass::parse(arch, name)?) } - InlineAsmArch::Nvptx64 => { - Self::Nvptx(NvptxInlineAsmRegClass::parse(arch, name)?) - } + InlineAsmArch::Nvptx64 => Self::Nvptx(NvptxInlineAsmRegClass::parse(arch, name)?), }) }) } From baa801a92900d1a44ab5efb005b4bbb0353af206 Mon Sep 17 00:00:00 2001 From: Michal Sudwoj Date: Fri, 22 May 2020 14:38:46 +0200 Subject: [PATCH 12/56] Minor fixes, as requested in PR review --- src/librustc_codegen_llvm/asm.rs | 4 -- src/librustc_target/asm/mod.rs | 28 +++++++++-- src/librustc_target/asm/nvptx.rs | 49 ++---------------- src/test/assembly/asm/nvptx-types.rs | 75 ++++++++++++++++++++-------- 4 files changed, 80 insertions(+), 76 deletions(-) diff --git a/src/librustc_codegen_llvm/asm.rs b/src/librustc_codegen_llvm/asm.rs index fe4cd16c1f5e..f3b46dd322a3 100644 --- a/src/librustc_codegen_llvm/asm.rs +++ b/src/librustc_codegen_llvm/asm.rs @@ -414,8 +414,6 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass) -> String { InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg16) => "h", InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg32) => "r", InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg64) => "l", - InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::freg32) => "f", - InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::freg64) => "d", InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => "r", InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => "f", InlineAsmRegClass::X86(X86InlineAsmRegClass::reg) => "r", @@ -512,8 +510,6 @@ fn dummy_output_type(cx: &CodegenCx<'ll, 'tcx>, reg: InlineAsmRegClass) -> &'ll InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg16) => cx.type_i16(), InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg32) => cx.type_i32(), InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg64) => cx.type_i64(), - InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::freg32) => cx.type_f32(), - InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::freg64) => cx.type_f64(), InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => cx.type_i32(), InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => cx.type_f32(), InlineAsmRegClass::X86(X86InlineAsmRegClass::reg) diff --git a/src/librustc_target/asm/mod.rs b/src/librustc_target/asm/mod.rs index 99061d1fdeb8..1bbaa4c4c002 100644 --- a/src/librustc_target/asm/mod.rs +++ b/src/librustc_target/asm/mod.rs @@ -52,6 +52,30 @@ macro_rules! def_reg_class { #[macro_use] macro_rules! def_regs { + ($arch:ident $arch_reg:ident $arch_regclass:ident {}) => { + #[allow(unreachable_code)] + #[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug, Eq, PartialEq, Hash, HashStable_Generic)] + pub enum $arch_reg {} + + impl $arch_reg { + pub fn parse( + _arch: super::InlineAsmArch, + mut _has_feature: impl FnMut(&str) -> bool, + _name: &str, + ) -> Result { + Err("unknown register") + } + } + + pub(super) fn fill_reg_map( + _arch: super::InlineAsmArch, + mut _has_feature: impl FnMut(&str) -> bool, + _map: &mut rustc_data_structures::fx::FxHashMap< + super::InlineAsmRegClass, + rustc_data_structures::fx::FxHashSet, + >, + ) {} + }; ($arch:ident $arch_reg:ident $arch_regclass:ident { $( $reg:ident: $class:ident $(, $extra_class:ident)* = [$reg_name:literal $(, $alias:literal)*] $(% $filter:ident)?, @@ -210,7 +234,6 @@ impl InlineAsmReg { Self::Arm(r) => r.name(), Self::AArch64(r) => r.name(), Self::RiscV(r) => r.name(), - Self::Nvptx(r) => r.name(), } } @@ -220,7 +243,6 @@ impl InlineAsmReg { Self::Arm(r) => InlineAsmRegClass::Arm(r.reg_class()), Self::AArch64(r) => InlineAsmRegClass::AArch64(r.reg_class()), Self::RiscV(r) => InlineAsmRegClass::RiscV(r.reg_class()), - Self::Nvptx(r) => InlineAsmRegClass::Nvptx(r.reg_class()), } } @@ -262,7 +284,6 @@ impl InlineAsmReg { Self::Arm(r) => r.emit(out, arch, modifier), Self::AArch64(r) => r.emit(out, arch, modifier), Self::RiscV(r) => r.emit(out, arch, modifier), - Self::Nvptx(r) => r.emit(out, arch, modifier), } } @@ -272,7 +293,6 @@ impl InlineAsmReg { Self::Arm(r) => r.overlapping_regs(|r| cb(Self::Arm(r))), Self::AArch64(_) => cb(self), Self::RiscV(_) => cb(self), - Self::Nvptx(_) => cb(self), } } } diff --git a/src/librustc_target/asm/nvptx.rs b/src/librustc_target/asm/nvptx.rs index b574e921718a..cba069cd0542 100644 --- a/src/librustc_target/asm/nvptx.rs +++ b/src/librustc_target/asm/nvptx.rs @@ -1,14 +1,11 @@ use super::{InlineAsmArch, InlineAsmType}; use rustc_macros::HashStable_Generic; -use std::fmt; def_reg_class! { Nvptx NvptxInlineAsmRegClass { reg16, reg32, reg64, - freg32, - freg64, } } @@ -39,52 +36,12 @@ impl NvptxInlineAsmRegClass { ) -> &'static [(InlineAsmType, Option<&'static str>)] { match self { Self::reg16 => types! { _: I8, I16; }, - Self::reg32 => types! { _: I8, I16, I32; }, - Self::reg64 => types! { _: I8, I16, I32, I64; }, - Self::freg32 => types! { _: F32; }, - Self::freg64 => types! { _: F32, F64; }, + Self::reg32 => types! { _: I8, I16, I32, F32; }, + Self::reg64 => types! { _: I8, I16, I32, F32, I64, F64; }, } } } def_regs! { - Nvptx NvptxInlineAsmReg NvptxInlineAsmRegClass { - // We have to define a register, otherwise we get warnings/errors about unused imports and - // unreachable code. Do what clang does and define r0. - r0: reg32 = ["r0"], - #error = ["tid", "tid.x", "tid.y", "tid.z"] => "tid not supported for inline asm", - #error = ["ntid", "ntid.x", "ntid.y", "ntid.z"] => "ntid not supported for inline asm", - #error = ["laneid"] => "laneid not supported for inline asm", - #error = ["warpid"] => "warpid not supported for inline asm", - #error = ["nwarpid"] => "nwarpid not supported for inline asm", - #error = ["ctaid", "ctaid.x", "ctaid.y", "ctaid.z"] => "ctaid not supported for inline asm", - #error = ["nctaid", "nctaid.x", "nctaid.y", "nctaid.z"] => "nctaid not supported for inline asm", - #error = ["smid"] => "smid not supported for inline asm", - #error = ["nsmid"] => "nsmid not supported for inline asm", - #error = ["gridid"] => "gridid not supported for inline asm", - #error = ["lanemask_eq"] => "lanemask_eq not supported for inline asm", - #error = ["lanemask_le"] => "lanemask_le not supported for inline asm", - #error = ["lanemask_lt"] => "lanemask_lt not supported for inline asm", - #error = ["lanemask_ge"] => "lanemask_ge not supported for inline asm", - #error = ["lanemask_gt"] => "lanemask_gt not supported for inline asm", - #error = ["clock", "clock_hi"] => "clock not supported for inline asm", - #error = ["clock64"] => "clock64 not supported for inline asm", - #error = ["pm0", "pm1", "pm2", "pm3", "pm4", "pm5", "pm6", "pm7"] => "pm not supported for inline asm", - #error = ["pm0_64", "pm1_64", "pm2_64", "pm3_64", "pm4_64", "pm5_64", "pm6_64", "pm7_64"] => "pm_64 not supported for inline asm", - #error = ["envreg0", "envreg1", "envreg2", "envreg3", "envreg4", "envreg5", "envreg6", "envreg7", "envreg8", "envreg9", "envreg10", "envreg11", "envreg12", "envreg13", "envreg14", "envreg15", "envreg16", "envreg17", "envreg18", "envreg19", "envreg20", "envreg21", "envreg22", "envreg23", "envreg24", "envreg25", "envreg26", "envreg27", "envreg28", "envreg29", "envreg30", "envreg31"] => "envreg not supported for inline asm", - #error = ["globaltimer", "globaltimer_lo", "globaltimer_hi"] => "globaltimer not supported for inline asm", - #error = ["total_mem_size"] => "total_mem_size not supported for inline asm", - #error = ["dynamic_mem_size"] => "dynamic_mem_size not supported for inline asm", - } -} - -impl NvptxInlineAsmReg { - pub fn emit( - self, - out: &mut dyn fmt::Write, - _arch: InlineAsmArch, - _modifier: Option, - ) -> fmt::Result { - out.write_str(self.name()) - } + Nvptx NvptxInlineAsmReg NvptxInlineAsmRegClass {} } diff --git a/src/test/assembly/asm/nvptx-types.rs b/src/test/assembly/asm/nvptx-types.rs index cfaab2351245..5faf4082f327 100644 --- a/src/test/assembly/asm/nvptx-types.rs +++ b/src/test/assembly/asm/nvptx-types.rs @@ -1,6 +1,7 @@ // no-system-llvm // assembly-output: emit-asm // compile-flags: --target --nvptx64-nvidia-cuda +// compile-flags: -Z merge-functions=disabled // only-nvptx64 // ignore-nvptx64 @@ -53,12 +54,6 @@ macro_rules! check { ($func:ident $ty:ident, $class:ident $mov:literal) => { #[no_mangle] pub unsafe fn $func(x: $ty) -> $ty { - // Hack to avoid function merging - extern "Rust" { - fn dont_merge(s: &str); - } - dont_merge(stringify!($func)); - let y; asm!(concat!($mov, " {}, {};"), out($class) y, in($class) x); y @@ -66,44 +61,80 @@ macro_rules! check { }; } -// CHECK-LABEL: reg_i8 +// CHECK-LABEL: reg16_i8 // CHECK: #APP // CHECK: mov.i16 {{[a-z0-9]+}}, {{[a-z0-9]+}}; // CHECK: #NO_APP -check!(reg_i8 i8 reg16 "mov.i16"); +check!(reg16_i8 i8 reg16 "mov.i16"); -// CHECK-LABEL: reg_i16 +// CHECK-LABEL: reg16_i16 // CHECK: #APP // CHECK: mov.i16 {{[a-z0-9]+}}, {{[a-z0-9]+}}; // CHECK: #NO_APP -check!(reg_i16 i16 reg16 "mov.i16"); +check!(reg16_i16 i16 reg16 "mov.i16"); + +// CHECK-LABEL: reg32_i8 +// CHECK: #APP +// CHECK: mov.i32 {{[a-z0-9]+}}, {{[a-z0-9]+}}; +// CHECK: #NO_APP +check!(reg32_i8 i8 reg32 "mov.i32"); + +// CHECK-LABEL: reg32_i16 +// CHECK: #APP +// CHECK: mov.i32 {{[a-z0-9]+}}, {{[a-z0-9]+}}; +// CHECK: #NO_APP +check!(reg32_i16 i16 reg32 "mov.i32"); -// CHECK-LABEL: reg_i32 +// CHECK-LABEL: reg32_i32 // CHECK: #APP // CHECK: mov.i32 {{[a-z0-9]+}}, {{[a-z0-9]+}}; // CHECK: #NO_APP -check!(reg_i32 i32 reg32 "mov.i32"); +check!(reg32_i32 i32 reg32 "mov.i32"); -// CHECK-LABEL: reg_f32 +// CHECK-LABEL: reg32_f32 // CHECK: #APP -// CHECK: mov.f32 {{[a-z0-9]+}}, {{[a-z0-9]+}}; +// CHECK: mov.i32 {{[a-z0-9]+}}, {{[a-z0-9]+}}; +// CHECK: #NO_APP +check!(reg32_f32 f32 reg32 "mov.i32"); + +// CHECK-LABEL: reg64_i8 +// CHECK: #APP +// CHECK: mov.i64 {{[a-z0-9]+}}, {{[a-z0-9]+}}; // CHECK: #NO_APP -check!(reg_f32 f32 freg32 "mov.f32"); +check!(reg64_i8 i8 reg64 "mov.i64"); -// CHECK-LABEL: reg_i54 +// CHECK-LABEL: reg64_i16 // CHECK: #APP // CHECK: mov.i64 {{[a-z0-9]+}}, {{[a-z0-9]+}}; // CHECK: #NO_APP -check!(reg_i64 i64 reg64 "mov.i64"); +check!(reg64_i16 i16 reg64 "mov.i64"); -// CHECK-LABEL: reg_f64 +// CHECK-LABEL: reg64_i32 // CHECK: #APP -// CHECK: mov.f64 {{[a-z0-9]+}}, {{[a-z0-9]+}}; +// CHECK: mov.i64 {{[a-z0-9]+}}, {{[a-z0-9]+}}; +// CHECK: #NO_APP +check!(reg64_i32 i32 reg64 "mov.i64"); + +// CHECK-LABEL: reg64_f32 +// CHECK: #APP +// CHECK: mov.i64 {{[a-z0-9]+}}, {{[a-z0-9]+}}; +// CHECK: #NO_APP +check!(reg64_f32 f32 reg64 "mov.i64"); + +// CHECK-LABEL: reg64_i64 +// CHECK: #APP +// CHECK: mov.i64 {{[a-z0-9]+}}, {{[a-z0-9]+}}; +// CHECK: #NO_APP +check!(reg64_i64 i64 reg64 "mov.i64"); + +// CHECK-LABEL: reg64_f64 +// CHECK: #APP +// CHECK: mov.i64 {{[a-z0-9]+}}, {{[a-z0-9]+}}; // CHECK: #NO_APP -check!(reg_f64 f64 freg64 "mov.f64"); +check!(reg64_f64 f64 reg64 "mov.i64"); -// CHECK-LABEL: reg_ptr +// CHECK-LABEL: reg64_ptr // CHECK: #APP // CHECK: mov.i64 {{[a-z0-9]+}}, {{[a-z0-9]+}}; // CHECK: #NO_APP -check!(reg_ptr ptr reg64 "mov.i64"); +check!(reg64_ptr ptr reg64 "mov.i64"); From 1070f08aaba30cfe2aba587a911fcbdf35f6cdbc Mon Sep 17 00:00:00 2001 From: Michal Sudwoj Date: Fri, 22 May 2020 18:16:26 +0200 Subject: [PATCH 13/56] Deduplicated macro code --- src/librustc_target/asm/mod.rs | 32 +++++--------------------------- 1 file changed, 5 insertions(+), 27 deletions(-) diff --git a/src/librustc_target/asm/mod.rs b/src/librustc_target/asm/mod.rs index 1bbaa4c4c002..a18a4dbd3e21 100644 --- a/src/librustc_target/asm/mod.rs +++ b/src/librustc_target/asm/mod.rs @@ -52,30 +52,6 @@ macro_rules! def_reg_class { #[macro_use] macro_rules! def_regs { - ($arch:ident $arch_reg:ident $arch_regclass:ident {}) => { - #[allow(unreachable_code)] - #[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug, Eq, PartialEq, Hash, HashStable_Generic)] - pub enum $arch_reg {} - - impl $arch_reg { - pub fn parse( - _arch: super::InlineAsmArch, - mut _has_feature: impl FnMut(&str) -> bool, - _name: &str, - ) -> Result { - Err("unknown register") - } - } - - pub(super) fn fill_reg_map( - _arch: super::InlineAsmArch, - mut _has_feature: impl FnMut(&str) -> bool, - _map: &mut rustc_data_structures::fx::FxHashMap< - super::InlineAsmRegClass, - rustc_data_structures::fx::FxHashSet, - >, - ) {} - }; ($arch:ident $arch_reg:ident $arch_regclass:ident { $( $reg:ident: $class:ident $(, $extra_class:ident)* = [$reg_name:literal $(, $alias:literal)*] $(% $filter:ident)?, @@ -84,6 +60,7 @@ macro_rules! def_regs { #error = [$($bad_reg:literal),+] => $error:literal, )* }) => { + #[allow(unreachable_code)] #[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug, Eq, PartialEq, Hash, HashStable_Generic)] #[allow(non_camel_case_types)] pub enum $arch_reg { @@ -126,19 +103,20 @@ macro_rules! def_regs { pub(super) fn fill_reg_map( _arch: super::InlineAsmArch, mut _has_feature: impl FnMut(&str) -> bool, - map: &mut rustc_data_structures::fx::FxHashMap< + _map: &mut rustc_data_structures::fx::FxHashMap< super::InlineAsmRegClass, rustc_data_structures::fx::FxHashSet, >, ) { + #[allow(unused_imports)] use super::{InlineAsmReg, InlineAsmRegClass}; $( if $($filter(_arch, &mut _has_feature, true).is_ok() &&)? true { - if let Some(set) = map.get_mut(&InlineAsmRegClass::$arch($arch_regclass::$class)) { + if let Some(set) = _map.get_mut(&InlineAsmRegClass::$arch($arch_regclass::$class)) { set.insert(InlineAsmReg::$arch($arch_reg::$reg)); } $( - if let Some(set) = map.get_mut(&InlineAsmRegClass::$arch($arch_regclass::$extra_class)) { + if let Some(set) = _map.get_mut(&InlineAsmRegClass::$arch($arch_regclass::$extra_class)) { set.insert(InlineAsmReg::$arch($arch_reg::$reg)); } )* From 6d74e096d868385441874346c0eab93bc405ebef Mon Sep 17 00:00:00 2001 From: Michal Sudwoj Date: Fri, 22 May 2020 18:16:57 +0200 Subject: [PATCH 14/56] Added comment on there being no predefined registers --- src/librustc_target/asm/nvptx.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/librustc_target/asm/nvptx.rs b/src/librustc_target/asm/nvptx.rs index cba069cd0542..43d16ae0f5d1 100644 --- a/src/librustc_target/asm/nvptx.rs +++ b/src/librustc_target/asm/nvptx.rs @@ -43,5 +43,7 @@ impl NvptxInlineAsmRegClass { } def_regs! { + // Registers in PTX are declared in the assembly. + // There are no predefined registers that one can use. Nvptx NvptxInlineAsmReg NvptxInlineAsmRegClass {} } From 5ec6b5eaeed5cddcb224cdec7bda148b3c8631f4 Mon Sep 17 00:00:00 2001 From: Michal Sudwoj Date: Fri, 22 May 2020 18:17:22 +0200 Subject: [PATCH 15/56] Fixed tests --- src/test/assembly/asm/nvptx-types.rs | 130 +++++++++++++-------------- 1 file changed, 61 insertions(+), 69 deletions(-) diff --git a/src/test/assembly/asm/nvptx-types.rs b/src/test/assembly/asm/nvptx-types.rs index 5faf4082f327..b36efe16c64a 100644 --- a/src/test/assembly/asm/nvptx-types.rs +++ b/src/test/assembly/asm/nvptx-types.rs @@ -1,14 +1,10 @@ // no-system-llvm // assembly-output: emit-asm -// compile-flags: --target --nvptx64-nvidia-cuda -// compile-flags: -Z merge-functions=disabled -// only-nvptx64 -// ignore-nvptx64 +// compile-flags: --target nvptx64-nvidia-cuda +// compile-flags: --crate-type cdylib #![feature(no_core, lang_items, rustc_attrs)] -#![crate_type = "rlib"] #![no_core] -#![allow(asm_sub_register, non_camel_case_types)] #[rustc_builtin_macro] macro_rules! asm { @@ -18,10 +14,6 @@ macro_rules! asm { macro_rules! concat { () => {}; } -#[rustc_builtin_macro] -macro_rules! stringify { - () => {}; -} #[lang = "sized"] trait Sized {} @@ -39,19 +31,19 @@ impl Copy for f64 {} impl Copy for ptr {} #[no_mangle] -fn extern_func(); +fn extern_func() {} -// CHECK-LABEL: sym_fn -// CHECK: #APP -// CHECK call extern_func; -// CHECK: #NO_APP +// CHECK-LABEL: .visible .func sym_fn() +// CHECK: // begin inline asm +// CHECK: call extern_func; +// CHECK: // end inline asm #[no_mangle] pub unsafe fn sym_fn() { - asm!("call {}", sym extern_func); + asm!("call {};", sym extern_func); } macro_rules! check { - ($func:ident $ty:ident, $class:ident $mov:literal) => { + ($func:ident $ty:ident $class:ident $mov:literal) => { #[no_mangle] pub unsafe fn $func(x: $ty) -> $ty { let y; @@ -61,80 +53,80 @@ macro_rules! check { }; } -// CHECK-LABEL: reg16_i8 -// CHECK: #APP -// CHECK: mov.i16 {{[a-z0-9]+}}, {{[a-z0-9]+}}; -// CHECK: #NO_APP +// CHECK-LABEL: .visible .func (.param .b32 func_retval0) reg16_i8 +// CHECK: // begin inline asm +// CHECK: mov.i16 %{{[a-z0-9]+}}, %{{[a-z0-9]+}}; +// CHECK: // end inline asm check!(reg16_i8 i8 reg16 "mov.i16"); -// CHECK-LABEL: reg16_i16 -// CHECK: #APP -// CHECK: mov.i16 {{[a-z0-9]+}}, {{[a-z0-9]+}}; -// CHECK: #NO_APP +// CHECK-LABEL: .visible .func (.param .b32 func_retval0) reg16_i16 +// CHECK: // begin inline asm +// CHECK: mov.i16 %{{[a-z0-9]+}}, %{{[a-z0-9]+}}; +// CHECK: // end inline asm check!(reg16_i16 i16 reg16 "mov.i16"); -// CHECK-LABEL: reg32_i8 -// CHECK: #APP -// CHECK: mov.i32 {{[a-z0-9]+}}, {{[a-z0-9]+}}; -// CHECK: #NO_APP +// CHECK-LABEL: .visible .func (.param .b32 func_retval0) reg32_i8 +// CHECK: // begin inline asm +// CHECK: mov.i32 %{{[a-z0-9]+}}, %{{[a-z0-9]+}}; +// CHECK: // end inline asm check!(reg32_i8 i8 reg32 "mov.i32"); -// CHECK-LABEL: reg32_i16 -// CHECK: #APP -// CHECK: mov.i32 {{[a-z0-9]+}}, {{[a-z0-9]+}}; -// CHECK: #NO_APP +// CHECK-LABEL: .visible .func (.param .b32 func_retval0) reg32_i16 +// CHECK: // begin inline asm +// CHECK: mov.i32 %{{[a-z0-9]+}}, %{{[a-z0-9]+}}; +// CHECK: // end inline asm check!(reg32_i16 i16 reg32 "mov.i32"); -// CHECK-LABEL: reg32_i32 -// CHECK: #APP -// CHECK: mov.i32 {{[a-z0-9]+}}, {{[a-z0-9]+}}; -// CHECK: #NO_APP +// CHECK-LABEL: .visible .func (.param .b32 func_retval0) reg32_i32 +// CHECK: // begin inline asm +// CHECK: mov.i32 %{{[a-z0-9]+}}, %{{[a-z0-9]+}}; +// CHECK: // end inline asm check!(reg32_i32 i32 reg32 "mov.i32"); -// CHECK-LABEL: reg32_f32 -// CHECK: #APP -// CHECK: mov.i32 {{[a-z0-9]+}}, {{[a-z0-9]+}}; -// CHECK: #NO_APP +// CHECK-LABEL: .visible .func (.param .b32 func_retval0) reg32_f32 +// CHECK: // begin inline asm +// CHECK: mov.i32 %{{[a-z0-9]+}}, %{{[a-z0-9]+}}; +// CHECK: // end inline asm check!(reg32_f32 f32 reg32 "mov.i32"); -// CHECK-LABEL: reg64_i8 -// CHECK: #APP -// CHECK: mov.i64 {{[a-z0-9]+}}, {{[a-z0-9]+}}; -// CHECK: #NO_APP +// CHECK-LABEL: .visible .func (.param .b32 func_retval0) reg64_i8 +// CHECK: // begin inline asm +// CHECK: mov.i64 %{{[a-z0-9]+}}, %{{[a-z0-9]+}}; +// CHECK: // end inline asm check!(reg64_i8 i8 reg64 "mov.i64"); -// CHECK-LABEL: reg64_i16 -// CHECK: #APP -// CHECK: mov.i64 {{[a-z0-9]+}}, {{[a-z0-9]+}}; -// CHECK: #NO_APP +// CHECK-LABEL: .visible .func (.param .b32 func_retval0) reg64_i16 +// CHECK: // begin inline asm +// CHECK: mov.i64 %{{[a-z0-9]+}}, %{{[a-z0-9]+}}; +// CHECK: // end inline asm check!(reg64_i16 i16 reg64 "mov.i64"); -// CHECK-LABEL: reg64_i32 -// CHECK: #APP -// CHECK: mov.i64 {{[a-z0-9]+}}, {{[a-z0-9]+}}; -// CHECK: #NO_APP +// CHECK-LABEL: .visible .func (.param .b32 func_retval0) reg64_i32 +// CHECK: // begin inline asm +// CHECK: mov.i64 %{{[a-z0-9]+}}, %{{[a-z0-9]+}}; +// CHECK: // end inline asm check!(reg64_i32 i32 reg64 "mov.i64"); -// CHECK-LABEL: reg64_f32 -// CHECK: #APP -// CHECK: mov.i64 {{[a-z0-9]+}}, {{[a-z0-9]+}}; -// CHECK: #NO_APP +// CHECK-LABEL: .visible .func (.param .b32 func_retval0) reg64_f32 +// CHECK: // begin inline asm +// CHECK: mov.i64 %{{[a-z0-9]+}}, %{{[a-z0-9]+}}; +// CHECK: // end inline asm check!(reg64_f32 f32 reg64 "mov.i64"); -// CHECK-LABEL: reg64_i64 -// CHECK: #APP -// CHECK: mov.i64 {{[a-z0-9]+}}, {{[a-z0-9]+}}; -// CHECK: #NO_APP +// CHECK-LABEL: .visible .func (.param .b64 func_retval0) reg64_i64 +// CHECK: // begin inline asm +// CHECK: mov.i64 %{{[a-z0-9]+}}, %{{[a-z0-9]+}}; +// CHECK: // end inline asm check!(reg64_i64 i64 reg64 "mov.i64"); -// CHECK-LABEL: reg64_f64 -// CHECK: #APP -// CHECK: mov.i64 {{[a-z0-9]+}}, {{[a-z0-9]+}}; -// CHECK: #NO_APP +// CHECK-LABEL: .visible .func (.param .b64 func_retval0) reg64_f64 +// CHECK: // begin inline asm +// CHECK: mov.i64 %{{[a-z0-9]+}}, %{{[a-z0-9]+}}; +// CHECK: // end inline asm check!(reg64_f64 f64 reg64 "mov.i64"); -// CHECK-LABEL: reg64_ptr -// CHECK: #APP -// CHECK: mov.i64 {{[a-z0-9]+}}, {{[a-z0-9]+}}; -// CHECK: #NO_APP +// CHECK-LABEL: .visible .func (.param .b64 func_retval0) reg64_ptr +// CHECK: // begin inline asm +// CHECK: mov.i64 %{{[a-z0-9]+}}, %{{[a-z0-9]+}}; +// CHECK: // end inline asm check!(reg64_ptr ptr reg64 "mov.i64"); From ed559b3770d11f1047f0f4df3036beccb1b6dc47 Mon Sep 17 00:00:00 2001 From: Michal Sudwoj Date: Sat, 23 May 2020 00:25:20 +0200 Subject: [PATCH 16/56] Updated documentation --- src/doc/unstable-book/src/library-features/asm.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/doc/unstable-book/src/library-features/asm.md b/src/doc/unstable-book/src/library-features/asm.md index 0b68991fce2a..c34a2931bc06 100644 --- a/src/doc/unstable-book/src/library-features/asm.md +++ b/src/doc/unstable-book/src/library-features/asm.md @@ -468,12 +468,17 @@ Here is the list of currently supported register classes: | ARM | `qreg` | `q[0-15]` | `w` | | ARM | `qreg_low8` | `q[0-7]` | `t` | | ARM | `qreg_low4` | `q[0-3]` | `x` | +| NVPTX | `reg16` | None\* | `h` | +| NVPTX | `reg32` | None\* | `r` | +| NVPTX | `reg64` | None\* | `l` | | RISC-V | `reg` | `x1`, `x[5-7]`, `x[9-15]`, `x[16-31]` (non-RV32E) | `r` | | RISC-V | `freg` | `f[0-31]` | `f` | > **Note**: On x86 we treat `reg_byte` differently from `reg` because the compiler can allocate `al` and `ah` separately whereas `reg` reserves the whole register. > > Note #2: On x86-64 the high byte registers (e.g. `ah`) are only available when used as an explicit register. Specifying the `reg_byte` register class for an operand will always allocate a low byte register. +> +> Note #3: On NVPTX, register names are not supported. Additional register classes may be added in the future based on demand (e.g. MMX, x87, etc). @@ -495,6 +500,9 @@ Each register class has constraints on which value types they can be used with. | ARM | `sreg` | `vfp2` | `i32`, `f32` | | ARM | `dreg` | `vfp2` | `i64`, `f64`, `i8x8`, `i16x4`, `i32x2`, `i64x1`, `f32x2` | | ARM | `qreg` | `neon` | `i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4` | +| NVPTX | `reg16` | None | `i8`, `i16` | +| NVPTX | `reg32` | None | `i8`, `i16`, `i32`, `f32` | +| NVPTX | `reg64` | None | `i8`, `i16`, `i32`, `f32`, `i64`, `f64` | | RISC-V32 | `reg` | None | `i8`, `i16`, `i32`, `f32` | | RISC-V64 | `reg` | None | `i8`, `i16`, `i32`, `f32`, `i64`, `f64` | | RISC-V | `freg` | `f` | `f32` | @@ -502,7 +510,7 @@ Each register class has constraints on which value types they can be used with. > **Note**: For the purposes of the above table pointers, function pointers and `isize`/`usize` are treated as the equivalent integer type (`i16`/`i32`/`i64` depending on the target). -If a value is of a smaller size than the register it is allocated in then the upper bits of that register will have an undefined value for inputs and will be ignored for outputs. The only exception is the `freg` register class on RISC-V where `f32` values are NaN-boxed in a `f64` as required by the RISC-V architecture. +If a value is of a smaller size than the register it is allocated in then the upper bits of that register will have an undefined value for inputs and will be ignored for outputs. The only exceptions are the `freg` register class on RISC-V where `f32` values are NaN-boxed in a `f64` as required by the RISC-V architecture, and the register classes on NVPTX where values are zero-extended. When separate input and output expressions are specified for an `inout` operand, both expressions must have the same type. The only exception is if both operands are pointers or integers, in which case they are only required to have the same size. This restriction exists because the register allocators in LLVM and GCC sometimes cannot handle tied operands with different types. @@ -610,6 +618,9 @@ The supported modifiers are a subset of LLVM's (and GCC's) [asm template argumen | ARM | `dreg` | None | `d0` | `P` | | ARM | `qreg` | None | `q0` | `q` | | ARM | `qreg` | `e` / `f` | `d0` / `d1` | `e` / `f` | +| NVPTX | `reg16` | None | `rs0` | None | +| NVPTX | `reg32` | None | `r0` | None | +| NVPTX | `reg64` | None | `rd0` | None | | RISC-V | `reg` | None | `x1` | None | | RISC-V | `freg` | None | `f0` | None | From 70cd375c57bc35db9c9a3eff37abdb900e45f910 Mon Sep 17 00:00:00 2001 From: Michal Sudwoj Date: Sat, 23 May 2020 03:48:40 +0200 Subject: [PATCH 17/56] Corrected statement about zero-extension in docs. --- src/doc/unstable-book/src/library-features/asm.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/unstable-book/src/library-features/asm.md b/src/doc/unstable-book/src/library-features/asm.md index c34a2931bc06..50121b2c0fca 100644 --- a/src/doc/unstable-book/src/library-features/asm.md +++ b/src/doc/unstable-book/src/library-features/asm.md @@ -510,7 +510,7 @@ Each register class has constraints on which value types they can be used with. > **Note**: For the purposes of the above table pointers, function pointers and `isize`/`usize` are treated as the equivalent integer type (`i16`/`i32`/`i64` depending on the target). -If a value is of a smaller size than the register it is allocated in then the upper bits of that register will have an undefined value for inputs and will be ignored for outputs. The only exceptions are the `freg` register class on RISC-V where `f32` values are NaN-boxed in a `f64` as required by the RISC-V architecture, and the register classes on NVPTX where values are zero-extended. +If a value is of a smaller size than the register it is allocated in then the upper bits of that register will have an undefined value for inputs and will be ignored for outputs. The only exception is the `freg` register class on RISC-V where `f32` values are NaN-boxed in a `f64` as required by the RISC-V architecture. When separate input and output expressions are specified for an `inout` operand, both expressions must have the same type. The only exception is if both operands are pointers or integers, in which case they are only required to have the same size. This restriction exists because the register allocators in LLVM and GCC sometimes cannot handle tied operands with different types. From 83a5cdf0aec1a998fed98565829ee7b732b95134 Mon Sep 17 00:00:00 2001 From: Michal Sudwoj Date: Sat, 23 May 2020 03:55:34 +0200 Subject: [PATCH 18/56] Update src/doc/unstable-book/src/library-features/asm.md Co-authored-by: Amanieu d'Antras --- src/doc/unstable-book/src/library-features/asm.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/unstable-book/src/library-features/asm.md b/src/doc/unstable-book/src/library-features/asm.md index 50121b2c0fca..ea560a6d7091 100644 --- a/src/doc/unstable-book/src/library-features/asm.md +++ b/src/doc/unstable-book/src/library-features/asm.md @@ -478,7 +478,7 @@ Here is the list of currently supported register classes: > > Note #2: On x86-64 the high byte registers (e.g. `ah`) are only available when used as an explicit register. Specifying the `reg_byte` register class for an operand will always allocate a low byte register. > -> Note #3: On NVPTX, register names are not supported. +> Note #3: NVPTX doesn't have a fixed register set, so named registers are not supported. Additional register classes may be added in the future based on demand (e.g. MMX, x87, etc). From e18054d5c07c2303380b47c3ea21f5bd7cccddc9 Mon Sep 17 00:00:00 2001 From: Michal Sudwoj Date: Sat, 23 May 2020 13:13:17 +0200 Subject: [PATCH 19/56] Added comment about static variables --- src/test/assembly/asm/nvptx-types.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/assembly/asm/nvptx-types.rs b/src/test/assembly/asm/nvptx-types.rs index b36efe16c64a..4ee79d1bcc83 100644 --- a/src/test/assembly/asm/nvptx-types.rs +++ b/src/test/assembly/asm/nvptx-types.rs @@ -30,6 +30,7 @@ impl Copy for i64 {} impl Copy for f64 {} impl Copy for ptr {} +// NVPTX does not support static variables #[no_mangle] fn extern_func() {} From 08df3116e92356311735be2d0c588d461e16fbff Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Fri, 24 Apr 2020 00:17:29 -0700 Subject: [PATCH 20/56] librustc_mir: Add support for const fn offset/arith_offset Miri's pointer_offset_inbounds implementation has been moved into librustc_mir as ptr_offset_inbounds (to avoid breaking miri on a nightly update). The comments have been slightly reworked to better match `offset`'s external documentation about what causes UB. The intrinsic implementations are taken directly from miri. Signed-off-by: Joe Richey --- src/librustc_mir/interpret/intrinsics.rs | 53 +++++++++++++++++++++++- src/librustc_span/symbol.rs | 2 + 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs index fc4be82ad90a..0db9c7026588 100644 --- a/src/librustc_mir/interpret/intrinsics.rs +++ b/src/librustc_mir/interpret/intrinsics.rs @@ -10,11 +10,11 @@ use rustc_middle::mir::{ }; use rustc_middle::ty; use rustc_middle::ty::subst::SubstsRef; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{Ty, TyCtxt}; use rustc_span::symbol::{sym, Symbol}; use rustc_target::abi::{Abi, LayoutOf as _, Primitive, Size}; -use super::{ImmTy, InterpCx, Machine, OpTy, PlaceTy}; +use super::{CheckInAllocMsg, ImmTy, InterpCx, Machine, OpTy, PlaceTy}; mod caller_location; mod type_name; @@ -279,7 +279,24 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let result = Scalar::from_uint(truncated_bits, layout.size); self.write_scalar(result, dest)?; } + sym::offset => { + let ptr = self.read_scalar(args[0])?.not_undef()?; + let offset_count = self.read_scalar(args[1])?.to_machine_isize(self)?; + let pointee_ty = substs.type_at(0); + let offset_ptr = self.ptr_offset_inbounds(ptr, pointee_ty, offset_count)?; + self.write_scalar(offset_ptr, dest)?; + } + sym::arith_offset => { + let ptr = self.read_scalar(args[0])?.not_undef()?; + let offset_count = self.read_scalar(args[1])?.to_machine_isize(self)?; + let pointee_ty = substs.type_at(0); + + let pointee_size = i64::try_from(self.layout_of(pointee_ty)?.size.bytes()).unwrap(); + let offset_bytes = offset_count.wrapping_mul(pointee_size); + let offset_ptr = ptr.ptr_wrapping_signed_offset(offset_bytes, self); + self.write_scalar(offset_ptr, dest)?; + } sym::ptr_offset_from => { let a = self.read_immediate(args[0])?.to_scalar()?; let b = self.read_immediate(args[1])?.to_scalar()?; @@ -409,4 +426,36 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // `Rem` says this is all right, so we can let `Div` do its job. self.binop_ignore_overflow(BinOp::Div, a, b, dest) } + + /// Offsets a pointer by some multiple of its type, returning an error if the pointer leaves its + /// allocation. For integer pointers, we consider each of them their own tiny allocation of size + /// 0, so offset-by-0 (and only 0) is okay -- except that NULL cannot be offset by _any_ value. + pub fn ptr_offset_inbounds( + &self, + ptr: Scalar, + pointee_ty: Ty<'tcx>, + offset_count: i64, + ) -> InterpResult<'tcx, Scalar> { + let pointee_size = i64::try_from(self.layout_of(pointee_ty)?.size.bytes()).unwrap(); + // The computed offset, in bytes, cannot overflow an isize. + let offset_bytes = offset_count + .checked_mul(pointee_size) + .ok_or(err_ub_format!("inbounds pointer arithmetic: overflow computing offset"))?; + // The offset being in bounds cannot rely on "wrapping around" the address space. + // So, first rule out overflows in the pointer arithmetic. + let offset_ptr = ptr.ptr_signed_offset(offset_bytes, self)?; + // ptr and offset_ptr must be in bounds of the same allocated object. This means all of the + // memory between these pointers must be accessible. Note that we do not require the + // pointers to be properly aligned (unlike a read/write operation). + let min_ptr = if offset_bytes >= 0 { ptr } else { offset_ptr }; + let size = offset_bytes.checked_abs().unwrap(); + // This call handles checking for integer/NULL pointers. + self.memory.check_ptr_access_align( + min_ptr, + Size::from_bytes(size), + None, + CheckInAllocMsg::InboundsTest, + )?; + Ok(offset_ptr) + } } diff --git a/src/librustc_span/symbol.rs b/src/librustc_span/symbol.rs index 6a6098710e82..9b055beb99df 100644 --- a/src/librustc_span/symbol.rs +++ b/src/librustc_span/symbol.rs @@ -147,6 +147,7 @@ symbols! { Arc, Arguments, ArgumentV1, + arith_offset, arm_target_feature, asm, assert, @@ -516,6 +517,7 @@ symbols! { not, note, object_safe_for_dispatch, + offset, Ok, omit_gdb_pretty_printer_section, on, From 9b3dfd8ea987ce1d4b5ccbcb4c032f60a71c8cfb Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Fri, 24 Apr 2020 00:19:11 -0700 Subject: [PATCH 21/56] core: Make pointer offset methods "const fn" Signed-off-by: Joe Richey --- src/libcore/intrinsics.rs | 2 ++ src/libcore/lib.rs | 1 + src/libcore/ptr/const_ptr.rs | 18 ++++++++++++------ src/libcore/ptr/mut_ptr.rs | 18 ++++++++++++------ 4 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 9006e4cfaf7b..2d97fecf8a76 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -1314,6 +1314,7 @@ extern "rust-intrinsic" { /// The stabilized version of this intrinsic is /// [`std::pointer::offset`](../../std/primitive.pointer.html#method.offset). #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] pub fn offset(dst: *const T, offset: isize) -> *const T; /// Calculates the offset from a pointer, potentially wrapping. @@ -1331,6 +1332,7 @@ extern "rust-intrinsic" { /// The stabilized version of this intrinsic is /// [`std::pointer::wrapping_offset`](../../std/primitive.pointer.html#method.wrapping_offset). #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] pub fn arith_offset(dst: *const T, offset: isize) -> *const T; /// Equivalent to the appropriate `llvm.memcpy.p0i8.0i8.*` intrinsic, with diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index ca13433caec8..7d21f9a9a66d 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -85,6 +85,7 @@ #![feature(const_panic)] #![feature(const_fn_union)] #![feature(const_generics)] +#![feature(const_ptr_offset)] #![feature(const_ptr_offset_from)] #![feature(const_result)] #![feature(const_slice_from_raw_parts)] diff --git a/src/libcore/ptr/const_ptr.rs b/src/libcore/ptr/const_ptr.rs index 85ba5fc0638e..835183d171a7 100644 --- a/src/libcore/ptr/const_ptr.rs +++ b/src/libcore/ptr/const_ptr.rs @@ -151,8 +151,9 @@ impl *const T { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] #[inline] - pub unsafe fn offset(self, count: isize) -> *const T + pub const unsafe fn offset(self, count: isize) -> *const T where T: Sized, { @@ -210,8 +211,9 @@ impl *const T { /// ``` #[stable(feature = "ptr_wrapping_offset", since = "1.16.0")] #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] #[inline] - pub fn wrapping_offset(self, count: isize) -> *const T + pub const fn wrapping_offset(self, count: isize) -> *const T where T: Sized, { @@ -393,8 +395,9 @@ impl *const T { /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] #[inline] - pub unsafe fn add(self, count: usize) -> Self + pub const unsafe fn add(self, count: usize) -> Self where T: Sized, { @@ -455,8 +458,9 @@ impl *const T { /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] #[inline] - pub unsafe fn sub(self, count: usize) -> Self + pub const unsafe fn sub(self, count: usize) -> Self where T: Sized, { @@ -511,8 +515,9 @@ impl *const T { /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] #[inline] - pub fn wrapping_add(self, count: usize) -> Self + pub const fn wrapping_add(self, count: usize) -> Self where T: Sized, { @@ -567,8 +572,9 @@ impl *const T { /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] #[inline] - pub fn wrapping_sub(self, count: usize) -> Self + pub const fn wrapping_sub(self, count: usize) -> Self where T: Sized, { diff --git a/src/libcore/ptr/mut_ptr.rs b/src/libcore/ptr/mut_ptr.rs index 0781d7e6cac4..40b5e4e22340 100644 --- a/src/libcore/ptr/mut_ptr.rs +++ b/src/libcore/ptr/mut_ptr.rs @@ -145,8 +145,9 @@ impl *mut T { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] #[inline] - pub unsafe fn offset(self, count: isize) -> *mut T + pub const unsafe fn offset(self, count: isize) -> *mut T where T: Sized, { @@ -203,8 +204,9 @@ impl *mut T { /// ``` #[stable(feature = "ptr_wrapping_offset", since = "1.16.0")] #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] #[inline] - pub fn wrapping_offset(self, count: isize) -> *mut T + pub const fn wrapping_offset(self, count: isize) -> *mut T where T: Sized, { @@ -439,8 +441,9 @@ impl *mut T { /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] #[inline] - pub unsafe fn add(self, count: usize) -> Self + pub const unsafe fn add(self, count: usize) -> Self where T: Sized, { @@ -501,8 +504,9 @@ impl *mut T { /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] #[inline] - pub unsafe fn sub(self, count: usize) -> Self + pub const unsafe fn sub(self, count: usize) -> Self where T: Sized, { @@ -557,8 +561,9 @@ impl *mut T { /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] #[inline] - pub fn wrapping_add(self, count: usize) -> Self + pub const fn wrapping_add(self, count: usize) -> Self where T: Sized, { @@ -613,8 +618,9 @@ impl *mut T { /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] #[inline] - pub fn wrapping_sub(self, count: usize) -> Self + pub const fn wrapping_sub(self, count: usize) -> Self where T: Sized, { From 88a37a20867a3b9840d2f56f806f77ec0990d50c Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Fri, 15 May 2020 01:28:33 -0700 Subject: [PATCH 22/56] test/ui/consts: Add tests for const ptr offsets Signed-off-by: Joe Richey --- src/test/ui/consts/offset.rs | 115 +++++++++++++++++++++ src/test/ui/consts/offset_ub.rs | 22 ++++ src/test/ui/consts/offset_ub.stderr | 154 ++++++++++++++++++++++++++++ 3 files changed, 291 insertions(+) create mode 100644 src/test/ui/consts/offset.rs create mode 100644 src/test/ui/consts/offset_ub.rs create mode 100644 src/test/ui/consts/offset_ub.stderr diff --git a/src/test/ui/consts/offset.rs b/src/test/ui/consts/offset.rs new file mode 100644 index 000000000000..f64242d568e3 --- /dev/null +++ b/src/test/ui/consts/offset.rs @@ -0,0 +1,115 @@ +// run-pass +#![feature(const_ptr_offset)] +#![feature(const_ptr_offset_from)] +#![feature(ptr_offset_from)] +use std::ptr; + +#[repr(C)] +struct Struct { + a: u32, + b: u32, + c: u32, +} +static S: Struct = Struct { a: 0, b: 0, c: 0 }; + +// For these tests we use offset_from to check that two pointers are equal. +// Rust doesn't currently support comparing pointers in const fn. + +static OFFSET_NO_CHANGE: bool = unsafe { + let p1 = &S.b as *const u32; + let p2 = p1.offset(2).offset(-2); + p1.offset_from(p2) == 0 +}; +static OFFSET_MIDDLE: bool = unsafe { + let p1 = (&S.a as *const u32).offset(1); + let p2 = (&S.c as *const u32).offset(-1); + p1.offset_from(p2) == 0 +}; +// Pointing to the end of the allocation is OK +static OFFSET_END: bool = unsafe { + let p1 = (&S.a as *const u32).offset(3); + let p2 = (&S.c as *const u32).offset(1); + p1.offset_from(p2) == 0 +}; +// Casting though a differently sized type is OK +static OFFSET_U8_PTR: bool = unsafe { + let p1 = (&S.a as *const u32 as *const u8).offset(5); + let p2 = (&S.c as *const u32 as *const u8).offset(-3); + p1.offset_from(p2) == 0 +}; +// Any offset with a ZST does nothing +const OFFSET_ZST: bool = unsafe { + let pz = &() as *const (); + // offset_from can't work with ZSTs, so cast to u8 ptr + let p1 = pz.offset(5) as *const u8; + let p2 = pz.offset(isize::MIN) as *const u8; + p1.offset_from(p2) == 0 +}; +const OFFSET_ZERO: bool = unsafe { + let p = [0u8; 0].as_ptr(); + p.offset(0).offset_from(p) == 0 +}; +const OFFSET_ONE: bool = unsafe { + let p = &42u32 as *const u32; + p.offset(1).offset_from(p) == 1 +}; +const OFFSET_DANGLING: bool = unsafe { + let p = ptr::NonNull::::dangling().as_ptr(); + p.offset(0).offset_from(p) == 0 +}; +const OFFSET_UNALIGNED: bool = unsafe { + let arr = [0u8; 32]; + let p1 = arr.as_ptr(); + let p2 = (p1.offset(2) as *const u32).offset(1); + (p2 as *const u8).offset_from(p1) == 6 +}; + +const WRAP_OFFSET_NO_CHANGE: bool = unsafe { + let p1 = &42u32 as *const u32; + let p2 = p1.wrapping_offset(1000).wrapping_offset(-1000); + let p3 = p1.wrapping_offset(-1000).wrapping_offset(1000); + (p1.offset_from(p2) == 0) & (p1.offset_from(p3) == 0) +}; +const WRAP_ADDRESS_SPACE: bool = unsafe { + let p1 = &42u8 as *const u8; + let p2 = p1.wrapping_offset(isize::MIN).wrapping_offset(isize::MIN); + p1.offset_from(p2) == 0 +}; +// Wrap on the count*size_of::() calculation. +const WRAP_SIZE_OF: bool = unsafe { + // Make sure that if p1 moves backwards, we are still in range + let arr = [0u32; 2]; + let p = &arr[1] as *const u32; + // With wrapping arithmetic, isize::MAX * 4 == -4 + let wrapped = p.wrapping_offset(isize::MAX); + let backward = p.wrapping_offset(-1); + wrapped.offset_from(backward) == 0 +}; +const WRAP_INTEGER_POINTER: bool = unsafe { + let p1 = (0x42 as *const u32).wrapping_offset(4); + let p2 = 0x52 as *const u32; + p1.offset_from(p2) == 0 +}; +const WRAP_NULL: bool = unsafe { + let p1 = ptr::null::().wrapping_offset(1); + let p2 = 0x4 as *const u32; + p1.offset_from(p2) == 0 +}; + +fn main() { + assert!(OFFSET_NO_CHANGE); + assert!(OFFSET_MIDDLE); + assert!(OFFSET_END); + assert!(OFFSET_U8_PTR); + assert!(OFFSET_ZST); + assert!(OFFSET_ZERO); + assert!(OFFSET_ONE); + assert!(OFFSET_DANGLING); + assert!(OFFSET_UNALIGNED); + + assert!(WRAP_OFFSET_NO_CHANGE); + assert!(WRAP_ADDRESS_SPACE); + assert!(WRAP_SIZE_OF); + assert!(WRAP_INTEGER_POINTER); + assert!(WRAP_NULL); +} diff --git a/src/test/ui/consts/offset_ub.rs b/src/test/ui/consts/offset_ub.rs new file mode 100644 index 000000000000..ac1207896948 --- /dev/null +++ b/src/test/ui/consts/offset_ub.rs @@ -0,0 +1,22 @@ +// ignore-tidy-linelength +#![feature(const_ptr_offset)] +use std::ptr; + +// normalize-stderr-test "alloc\d+" -> "allocN" + +pub const BEFORE_START: *const u8 = unsafe { (&0u8 as *const u8).offset(-1) }; //~NOTE +pub const AFTER_END: *const u8 = unsafe { (&0u8 as *const u8).offset(2) }; //~NOTE +pub const AFTER_ARRAY: *const u8 = unsafe { [0u8; 100].as_ptr().offset(101) }; //~NOTE + +pub const OVERFLOW: *const u16 = unsafe { [0u16; 1].as_ptr().offset(isize::MAX) }; //~NOTE +pub const UNDERFLOW: *const u16 = unsafe { [0u16; 1].as_ptr().offset(isize::MIN) }; //~NOTE +pub const OVERFLOW_ADDRESS_SPACE: *const u8 = unsafe { (usize::MAX as *const u8).offset(2) }; //~NOTE +pub const UNDERFLOW_ADDRESS_SPACE: *const u8 = unsafe { (1 as *const u8).offset(-2) }; //~NOTE + +pub const ZERO_SIZED_ALLOC: *const u8 = unsafe { [0u8; 0].as_ptr().offset(1) }; //~NOTE +pub const DANGLING: *const u8 = unsafe { ptr::NonNull::::dangling().as_ptr().offset(4) }; //~NOTE + +// Right now, a zero offset from null is UB +pub const NULL_OFFSET_ZERO: *const u8 = unsafe { ptr::null::().offset(0) }; //~NOTE + +fn main() {} diff --git a/src/test/ui/consts/offset_ub.stderr b/src/test/ui/consts/offset_ub.stderr new file mode 100644 index 000000000000..b2b77586a504 --- /dev/null +++ b/src/test/ui/consts/offset_ub.stderr @@ -0,0 +1,154 @@ +error: any use of this value will cause an error + --> $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | +LL | intrinsics::offset(self, count) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | overflowing in-bounds pointer arithmetic + | inside `std::ptr::const_ptr::::offset` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `BEFORE_START` at $DIR/offset_ub.rs:7:46 + | + ::: $DIR/offset_ub.rs:7:1 + | +LL | pub const BEFORE_START: *const u8 = unsafe { (&0u8 as *const u8).offset(-1) }; + | ------------------------------------------------------------------------------ + | + = note: `#[deny(const_err)]` on by default + +error: any use of this value will cause an error + --> $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | +LL | intrinsics::offset(self, count) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | inbounds test failed: pointer must be in-bounds at offset 2, but is outside bounds of allocN which has size 1 + | inside `std::ptr::const_ptr::::offset` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `AFTER_END` at $DIR/offset_ub.rs:8:43 + | + ::: $DIR/offset_ub.rs:8:1 + | +LL | pub const AFTER_END: *const u8 = unsafe { (&0u8 as *const u8).offset(2) }; + | -------------------------------------------------------------------------- + +error: any use of this value will cause an error + --> $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | +LL | intrinsics::offset(self, count) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | inbounds test failed: pointer must be in-bounds at offset 101, but is outside bounds of allocN which has size 100 + | inside `std::ptr::const_ptr::::offset` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `AFTER_ARRAY` at $DIR/offset_ub.rs:9:45 + | + ::: $DIR/offset_ub.rs:9:1 + | +LL | pub const AFTER_ARRAY: *const u8 = unsafe { [0u8; 100].as_ptr().offset(101) }; + | ------------------------------------------------------------------------------ + +error: any use of this value will cause an error + --> $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | +LL | intrinsics::offset(self, count) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | inbounds pointer arithmetic: overflow computing offset + | inside `std::ptr::const_ptr::::offset` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `OVERFLOW` at $DIR/offset_ub.rs:11:43 + | + ::: $DIR/offset_ub.rs:11:1 + | +LL | pub const OVERFLOW: *const u16 = unsafe { [0u16; 1].as_ptr().offset(isize::MAX) }; + | ---------------------------------------------------------------------------------- + +error: any use of this value will cause an error + --> $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | +LL | intrinsics::offset(self, count) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | inbounds pointer arithmetic: overflow computing offset + | inside `std::ptr::const_ptr::::offset` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `UNDERFLOW` at $DIR/offset_ub.rs:12:44 + | + ::: $DIR/offset_ub.rs:12:1 + | +LL | pub const UNDERFLOW: *const u16 = unsafe { [0u16; 1].as_ptr().offset(isize::MIN) }; + | ----------------------------------------------------------------------------------- + +error: any use of this value will cause an error + --> $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | +LL | intrinsics::offset(self, count) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | overflowing in-bounds pointer arithmetic + | inside `std::ptr::const_ptr::::offset` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `OVERFLOW_ADDRESS_SPACE` at $DIR/offset_ub.rs:13:56 + | + ::: $DIR/offset_ub.rs:13:1 + | +LL | pub const OVERFLOW_ADDRESS_SPACE: *const u8 = unsafe { (usize::MAX as *const u8).offset(2) }; + | --------------------------------------------------------------------------------------------- + +error: any use of this value will cause an error + --> $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | +LL | intrinsics::offset(self, count) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | overflowing in-bounds pointer arithmetic + | inside `std::ptr::const_ptr::::offset` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `UNDERFLOW_ADDRESS_SPACE` at $DIR/offset_ub.rs:14:57 + | + ::: $DIR/offset_ub.rs:14:1 + | +LL | pub const UNDERFLOW_ADDRESS_SPACE: *const u8 = unsafe { (1 as *const u8).offset(-2) }; + | -------------------------------------------------------------------------------------- + +error: any use of this value will cause an error + --> $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | +LL | intrinsics::offset(self, count) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | inbounds test failed: pointer must be in-bounds at offset 1, but is outside bounds of allocN which has size 0 + | inside `std::ptr::const_ptr::::offset` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `ZERO_SIZED_ALLOC` at $DIR/offset_ub.rs:16:50 + | + ::: $DIR/offset_ub.rs:16:1 + | +LL | pub const ZERO_SIZED_ALLOC: *const u8 = unsafe { [0u8; 0].as_ptr().offset(1) }; + | ------------------------------------------------------------------------------- + +error: any use of this value will cause an error + --> $SRC_DIR/libcore/ptr/mut_ptr.rs:LL:COL + | +LL | intrinsics::offset(self, count) as *mut T + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | unable to turn bytes into a pointer + | inside `std::ptr::mut_ptr::::offset` at $SRC_DIR/libcore/ptr/mut_ptr.rs:LL:COL + | inside `DANGLING` at $DIR/offset_ub.rs:17:42 + | + ::: $DIR/offset_ub.rs:17:1 + | +LL | pub const DANGLING: *const u8 = unsafe { ptr::NonNull::::dangling().as_ptr().offset(4) }; + | --------------------------------------------------------------------------------------------- + +error: any use of this value will cause an error + --> $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | +LL | intrinsics::offset(self, count) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | inbounds test failed: 0x0 is not a valid pointer + | inside `std::ptr::const_ptr::::offset` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `NULL_OFFSET_ZERO` at $DIR/offset_ub.rs:20:50 + | + ::: $DIR/offset_ub.rs:20:1 + | +LL | pub const NULL_OFFSET_ZERO: *const u8 = unsafe { ptr::null::().offset(0) }; + | ------------------------------------------------------------------------------- + +error: aborting due to 10 previous errors + From 6b20f58c0d15500b2814a6f3579c29783463add0 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Mon, 18 May 2020 16:00:14 -0700 Subject: [PATCH 23/56] miri_unleached: We now allow offset in const fn Signed-off-by: Joe Richey --- .../ui/consts/miri_unleashed/ptr_arith.rs | 10 +-------- .../ui/consts/miri_unleashed/ptr_arith.stderr | 21 +++++-------------- 2 files changed, 6 insertions(+), 25 deletions(-) diff --git a/src/test/ui/consts/miri_unleashed/ptr_arith.rs b/src/test/ui/consts/miri_unleashed/ptr_arith.rs index 81985f9f625a..65fc49c0b27a 100644 --- a/src/test/ui/consts/miri_unleashed/ptr_arith.rs +++ b/src/test/ui/consts/miri_unleashed/ptr_arith.rs @@ -2,8 +2,7 @@ #![feature(core_intrinsics)] #![allow(const_err)] -// A test demonstrating that we prevent doing even trivial -// pointer arithmetic or comparison during CTFE. +// During CTFE, we prevent pointer comparison and pointer-to-int casts. static CMP: () = { let x = &0 as *const _; @@ -19,11 +18,4 @@ static INT_PTR_ARITH: () = unsafe { //~| NOTE pointer-to-integer cast }; -static PTR_ARITH: () = unsafe { - let x = &0 as *const _; - let _v = core::intrinsics::offset(x, 0); - //~^ ERROR could not evaluate static initializer - //~| NOTE calling intrinsic `offset` -}; - fn main() {} diff --git a/src/test/ui/consts/miri_unleashed/ptr_arith.stderr b/src/test/ui/consts/miri_unleashed/ptr_arith.stderr index 5bd534a16b86..805ba9c6b030 100644 --- a/src/test/ui/consts/miri_unleashed/ptr_arith.stderr +++ b/src/test/ui/consts/miri_unleashed/ptr_arith.stderr @@ -1,39 +1,28 @@ error[E0080]: could not evaluate static initializer - --> $DIR/ptr_arith.rs:10:14 + --> $DIR/ptr_arith.rs:9:14 | LL | let _v = x == x; | ^^^^^^ "pointer arithmetic or comparison" needs an rfc before being allowed inside constants error[E0080]: could not evaluate static initializer - --> $DIR/ptr_arith.rs:17:14 + --> $DIR/ptr_arith.rs:16:14 | LL | let _v = x + 0; | ^^^^^ "pointer-to-integer cast" needs an rfc before being allowed inside constants -error[E0080]: could not evaluate static initializer - --> $DIR/ptr_arith.rs:24:14 - | -LL | let _v = core::intrinsics::offset(x, 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ "calling intrinsic `offset`" needs an rfc before being allowed inside constants - warning: skipping const checks | help: skipping check for `const_compare_raw_pointers` feature - --> $DIR/ptr_arith.rs:10:14 + --> $DIR/ptr_arith.rs:9:14 | LL | let _v = x == x; | ^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/ptr_arith.rs:16:20 + --> $DIR/ptr_arith.rs:15:20 | LL | let x: usize = std::mem::transmute(&0); | ^^^^^^^^^^^^^^^^^^^^^^^ -help: skipping check that does not even have a feature gate - --> $DIR/ptr_arith.rs:24:14 - | -LL | let _v = core::intrinsics::offset(x, 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 3 previous errors; 1 warning emitted +error: aborting due to 2 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0080`. From 55577b411cd829b6793be8fd0af0e8811ba89e5c Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Mon, 25 May 2020 13:40:01 -0700 Subject: [PATCH 24/56] librustc_mir: Add back use statement Signed-off-by: Joe Richey --- src/librustc_mir/interpret/intrinsics.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs index 0db9c7026588..b47480592625 100644 --- a/src/librustc_mir/interpret/intrinsics.rs +++ b/src/librustc_mir/interpret/intrinsics.rs @@ -2,6 +2,8 @@ //! looking at their MIR. Intrinsics/functions supported here are shared by CTFE //! and miri. +use std::convert::TryFrom; + use rustc_hir::def_id::DefId; use rustc_middle::mir::{ self, From 6367b544b781889abee296d34d2b7d353a6ae0f8 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Tue, 26 May 2020 01:57:49 -0700 Subject: [PATCH 25/56] librustc_middle: Add function for computing unsigned abs This is tricky to get right if we want to avoid panicking or wrapping. Signed-off-by: Joe Richey --- src/librustc_middle/mir/interpret/mod.rs | 9 +++++++++ src/librustc_middle/mir/interpret/pointer.rs | 13 +++++-------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/librustc_middle/mir/interpret/mod.rs b/src/librustc_middle/mir/interpret/mod.rs index d9e52af89007..061bc9750e1c 100644 --- a/src/librustc_middle/mir/interpret/mod.rs +++ b/src/librustc_middle/mir/interpret/mod.rs @@ -598,3 +598,12 @@ pub fn truncate(value: u128, size: Size) -> u128 { // Truncate (shift left to drop out leftover values, shift right to fill with zeroes). (value << shift) >> shift } + +/// Computes the unsigned absolute value without wrapping or panicking. +#[inline] +pub fn uabs(value: i64) -> u64 { + // The only tricky part here is if value == i64::MIN. In that case, + // wrapping_abs() returns i64::MIN == -2^63. Casting this value to a u64 + // gives 2^63, the correct value. + value.wrapping_abs() as u64 +} diff --git a/src/librustc_middle/mir/interpret/pointer.rs b/src/librustc_middle/mir/interpret/pointer.rs index 70cc546199b7..019c96bc511e 100644 --- a/src/librustc_middle/mir/interpret/pointer.rs +++ b/src/librustc_middle/mir/interpret/pointer.rs @@ -1,4 +1,4 @@ -use super::{AllocId, InterpResult}; +use super::{uabs, AllocId, InterpResult}; use rustc_macros::HashStable; use rustc_target::abi::{HasDataLayout, Size}; @@ -48,15 +48,12 @@ pub trait PointerArithmetic: HasDataLayout { #[inline] fn overflowing_signed_offset(&self, val: u64, i: i64) -> (u64, bool) { - if i < 0 { - // Trickery to ensure that `i64::MIN` works fine: compute `n = -i`. - // This formula only works for true negative values; it overflows for zero! - let n = u64::MAX - (i as u64) + 1; + let n = uabs(i); + if i >= 0 { + self.overflowing_offset(val, n) + } else { let res = val.overflowing_sub(n); self.truncate_to_ptr(res) - } else { - // `i >= 0`, so the cast is safe. - self.overflowing_offset(val, i as u64) } } From 71ef8414bd86cbd79b29f8b1a0145da96e2f2f09 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Tue, 26 May 2020 02:00:02 -0700 Subject: [PATCH 26/56] Add checks and tests for computing abs(offset_bytes) The previous code paniced if offset_bytes == i64::MIN. This commit: - Properly computes the absoulte value to avoid this panic - Adds a test for this edge case Signed-off-by: Joe Richey --- src/librustc_mir/interpret/intrinsics.rs | 5 +++-- src/test/ui/consts/offset_ub.rs | 3 +++ src/test/ui/consts/offset_ub.stderr | 17 ++++++++++++++++- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs index b47480592625..239115076bc4 100644 --- a/src/librustc_mir/interpret/intrinsics.rs +++ b/src/librustc_mir/interpret/intrinsics.rs @@ -7,7 +7,7 @@ use std::convert::TryFrom; use rustc_hir::def_id::DefId; use rustc_middle::mir::{ self, - interpret::{ConstValue, GlobalId, InterpResult, Scalar}, + interpret::{uabs, ConstValue, GlobalId, InterpResult, Scalar}, BinOp, }; use rustc_middle::ty; @@ -438,6 +438,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pointee_ty: Ty<'tcx>, offset_count: i64, ) -> InterpResult<'tcx, Scalar> { + // We cannot overflow i64 as a type's size must be <= isize::MAX. let pointee_size = i64::try_from(self.layout_of(pointee_ty)?.size.bytes()).unwrap(); // The computed offset, in bytes, cannot overflow an isize. let offset_bytes = offset_count @@ -450,7 +451,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // memory between these pointers must be accessible. Note that we do not require the // pointers to be properly aligned (unlike a read/write operation). let min_ptr = if offset_bytes >= 0 { ptr } else { offset_ptr }; - let size = offset_bytes.checked_abs().unwrap(); + let size: u64 = uabs(offset_bytes); // This call handles checking for integer/NULL pointers. self.memory.check_ptr_access_align( min_ptr, diff --git a/src/test/ui/consts/offset_ub.rs b/src/test/ui/consts/offset_ub.rs index ac1207896948..4f943ed9ad19 100644 --- a/src/test/ui/consts/offset_ub.rs +++ b/src/test/ui/consts/offset_ub.rs @@ -19,4 +19,7 @@ pub const DANGLING: *const u8 = unsafe { ptr::NonNull::::dangling().as_ptr() // Right now, a zero offset from null is UB pub const NULL_OFFSET_ZERO: *const u8 = unsafe { ptr::null::().offset(0) }; //~NOTE +// Make sure that we don't panic when computing abs(offset*size_of::()) +pub const UNDERFLOW_ABS: *const u8 = unsafe { (usize::MAX as *const u8).offset(isize::MIN) }; //~NOTE + fn main() {} diff --git a/src/test/ui/consts/offset_ub.stderr b/src/test/ui/consts/offset_ub.stderr index b2b77586a504..e808939682f3 100644 --- a/src/test/ui/consts/offset_ub.stderr +++ b/src/test/ui/consts/offset_ub.stderr @@ -150,5 +150,20 @@ LL | intrinsics::offset(self, count) LL | pub const NULL_OFFSET_ZERO: *const u8 = unsafe { ptr::null::().offset(0) }; | ------------------------------------------------------------------------------- -error: aborting due to 10 previous errors +error: any use of this value will cause an error + --> $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | +LL | intrinsics::offset(self, count) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | unable to turn bytes into a pointer + | inside `std::ptr::const_ptr::::offset` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `UNDERFLOW_ABS` at $DIR/offset_ub.rs:23:47 + | + ::: $DIR/offset_ub.rs:23:1 + | +LL | pub const UNDERFLOW_ABS: *const u8 = unsafe { (usize::MAX as *const u8).offset(isize::MIN) }; + | --------------------------------------------------------------------------------------------- + +error: aborting due to 11 previous errors From a977df35d160c1fe7040c76a9276b64e7a7aedec Mon Sep 17 00:00:00 2001 From: LeSeulArtichaut Date: Sun, 3 May 2020 23:11:34 +0200 Subject: [PATCH 27/56] Implement RFC 2585 --- src/librustc_feature/active.rs | 3 + src/librustc_middle/mir/mod.rs | 2 +- src/librustc_middle/mir/query.rs | 7 +++ src/librustc_mir/transform/check_unsafety.rs | 63 +++++++++++++++++--- src/librustc_mir_build/build/block.rs | 2 + src/librustc_session/lint/builtin.rs | 7 +++ src/librustc_span/symbol.rs | 1 + 7 files changed, 75 insertions(+), 10 deletions(-) diff --git a/src/librustc_feature/active.rs b/src/librustc_feature/active.rs index 90b2380d8645..fd35cb6c3f78 100644 --- a/src/librustc_feature/active.rs +++ b/src/librustc_feature/active.rs @@ -571,6 +571,9 @@ declare_features! ( /// Allows the use of `#[ffi_const]` on foreign functions. (active, ffi_const, "1.45.0", Some(58328), None), + /// No longer treat an unsafe function as an unsafe block. + (active, unsafe_block_in_unsafe_fn, "1.45.0", Some(71668), None), + // ------------------------------------------------------------------------- // feature-group-end: actual feature gates // ------------------------------------------------------------------------- diff --git a/src/librustc_middle/mir/mod.rs b/src/librustc_middle/mir/mod.rs index c279213e5bd0..243f81459e76 100644 --- a/src/librustc_middle/mir/mod.rs +++ b/src/librustc_middle/mir/mod.rs @@ -408,7 +408,7 @@ impl<'tcx> Body<'tcx> { } } -#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] +#[derive(Copy, Clone, PartialEq, Eq, Debug, RustcEncodable, RustcDecodable, HashStable)] pub enum Safety { Safe, /// Unsafe because of a PushUnsafeBlock diff --git a/src/librustc_middle/mir/query.rs b/src/librustc_middle/mir/query.rs index 63b8d8c8da78..f19270e5561f 100644 --- a/src/librustc_middle/mir/query.rs +++ b/src/librustc_middle/mir/query.rs @@ -15,10 +15,17 @@ use super::{Field, SourceInfo}; #[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, HashStable)] pub enum UnsafetyViolationKind { + /// Only permitted in regular `fn`s, prohibitted in `const fn`s. General, /// Permitted both in `const fn`s and regular `fn`s. GeneralAndConstFn, + /// Borrow of packed field. + /// Has to be handled as a lint for backwards compatibility. BorrowPacked(hir::HirId), + /// Unsafe operation in an `unsafe fn` but outside an `unsafe` block. + /// Has to be handled as a lint for backwards compatibility. + /// Should stay gated under `#![feature(unsafe_block_in_unsafe_fn)]`. + UnsafeFn(hir::HirId), } #[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, HashStable)] diff --git a/src/librustc_mir/transform/check_unsafety.rs b/src/librustc_mir/transform/check_unsafety.rs index a335fa2de411..bcadbd091076 100644 --- a/src/librustc_mir/transform/check_unsafety.rs +++ b/src/librustc_mir/transform/check_unsafety.rs @@ -9,7 +9,7 @@ use rustc_middle::mir::*; use rustc_middle::ty::cast::CastTy; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, TyCtxt}; -use rustc_session::lint::builtin::{SAFE_PACKED_BORROWS, UNUSED_UNSAFE}; +use rustc_session::lint::builtin::{SAFE_PACKED_BORROWS, UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE}; use rustc_span::symbol::{sym, Symbol}; use std::ops::Bound; @@ -351,14 +351,35 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> { violation.kind = UnsafetyViolationKind::General; } } + UnsafetyViolationKind::UnsafeFn(_) => { + bug!("`UnsafetyViolationKind::UnsafeFn` in an `Safe` context") + } + } + if !self.violations.contains(&violation) { + self.violations.push(violation) } + } + false + } + // With the RFC 2585, no longer allow `unsafe` operations in `unsafe fn`s + Safety::FnUnsafe if self.tcx.features().unsafe_block_in_unsafe_fn => { + for violation in violations { + let mut violation = *violation; + let lint_root = self.body.source_scopes[self.source_info.scope] + .local_data + .as_ref() + .assert_crate_local() + .lint_root; + + // FIXME(LeSeulArtichaut): what to do with `UnsafetyViolationKind::BorrowPacked`? + violation.kind = UnsafetyViolationKind::UnsafeFn(lint_root); if !self.violations.contains(&violation) { self.violations.push(violation) } } false } - // `unsafe` function bodies allow unsafe without additional unsafe blocks + // `unsafe` function bodies allow unsafe without additional unsafe blocks (before RFC 2585) Safety::BuiltinUnsafe | Safety::FnUnsafe => true, Safety::ExplicitUnsafe(hir_id) => { // mark unsafe block as used if there are any unsafe operations inside @@ -383,6 +404,9 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> { self.violations.push(violation) } } + UnsafetyViolationKind::UnsafeFn(_) => bug!( + "`UnsafetyViolationKind::UnsafeFn` in an `ExplicitUnsafe` context" + ), } } } @@ -575,9 +599,12 @@ fn is_enclosed( kind: hir::ItemKind::Fn(ref sig, _, _), .. })) = tcx.hir().find(parent_id) { - match sig.header.unsafety { - hir::Unsafety::Unsafe => Some(("fn".to_string(), parent_id)), - hir::Unsafety::Normal => None, + if sig.header.unsafety == hir::Unsafety::Unsafe + && !tcx.features().unsafe_block_in_unsafe_fn + { + Some(("fn".to_string(), parent_id)) + } else { + None } } else { is_enclosed(tcx, used_unsafe, parent_id) @@ -630,16 +657,20 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: DefId) { let UnsafetyCheckResult { violations, unsafe_blocks } = tcx.unsafety_check_result(def_id.expect_local()); + let or_block_msg = if tcx.features().unsafe_block_in_unsafe_fn { "" } else { " or block" }; + for &UnsafetyViolation { source_info, description, details, kind } in violations.iter() { // Report an error. match kind { UnsafetyViolationKind::GeneralAndConstFn | UnsafetyViolationKind::General => { + // once struct_span_err!( tcx.sess, source_info.span, E0133, - "{} is unsafe and requires unsafe function or block", - description + "{} is unsafe and requires unsafe function{}", + description, + or_block_msg, ) .span_label(source_info.span, &*description.as_str()) .note(&details.as_str()) @@ -655,8 +686,8 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: DefId) { source_info.span, |lint| { lint.build(&format!( - "{} is unsafe and requires unsafe function or block (error E0133)", - description + "{} is unsafe and requires unsafe function{} (error E0133)", + description, or_block_msg, )) .note(&details.as_str()) .emit() @@ -664,6 +695,20 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: DefId) { ) } } + UnsafetyViolationKind::UnsafeFn(lint_hir_id) => tcx.struct_span_lint_hir( + UNSAFE_OP_IN_UNSAFE_FN, + lint_hir_id, + source_info.span, + |lint| { + lint.build(&format!( + "{} is unsafe and requires unsafe block (error E0133)", + description + )) + .span_label(source_info.span, &*description.as_str()) + .note(&details.as_str()) + .emit(); + }, + ), } } diff --git a/src/librustc_mir_build/build/block.rs b/src/librustc_mir_build/build/block.rs index fa783ddcf409..b052c839152d 100644 --- a/src/librustc_mir_build/build/block.rs +++ b/src/librustc_mir_build/build/block.rs @@ -217,6 +217,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { assert_eq!(self.push_unsafe_count, 0); match self.unpushed_unsafe { Safety::Safe => {} + // no longer treat `unsafe fn`s as `unsafe` contexts (see RFC #2585) + Safety::FnUnsafe if self.hir.tcx().features().unsafe_block_in_unsafe_fn => {} _ => return, } self.unpushed_unsafe = Safety::ExplicitUnsafe(hir_id); diff --git a/src/librustc_session/lint/builtin.rs b/src/librustc_session/lint/builtin.rs index e55ddc26a944..7112ac35b082 100644 --- a/src/librustc_session/lint/builtin.rs +++ b/src/librustc_session/lint/builtin.rs @@ -526,6 +526,12 @@ declare_lint! { "using only a subset of a register for inline asm inputs", } +declare_lint! { + pub UNSAFE_OP_IN_UNSAFE_FN, + Allow, + "unsafe operations in unsafe functions without an explicit unsafe block are deprecated", +} + declare_lint_pass! { /// Does nothing as a lint pass, but registers some `Lint`s /// that are used by other parts of the compiler. @@ -597,6 +603,7 @@ declare_lint_pass! { SOFT_UNSTABLE, INLINE_NO_SANITIZE, ASM_SUB_REGISTER, + UNSAFE_OP_IN_UNSAFE_FN, ] } diff --git a/src/librustc_span/symbol.rs b/src/librustc_span/symbol.rs index 6a6098710e82..87952d409c89 100644 --- a/src/librustc_span/symbol.rs +++ b/src/librustc_span/symbol.rs @@ -806,6 +806,7 @@ symbols! { unmarked_api, unreachable_code, unrestricted_attribute_tokens, + unsafe_block_in_unsafe_fn, unsafe_no_drop_flag, unsized_locals, unsized_tuple_coercion, From 594c499db995abaacc10a74c0afbb9aeed48117d Mon Sep 17 00:00:00 2001 From: LeSeulArtichaut Date: Sun, 3 May 2020 23:11:58 +0200 Subject: [PATCH 28/56] Add tests --- .../feature-gate-unsafe_block_in_unsafe_fn.rs | 11 +++++++ ...ture-gate-unsafe_block_in_unsafe_fn.stderr | 16 ++++++++++ .../unsafe/rfc-2585-unsafe_op_in_unsafe_fn.rs | 26 +++++++++++++++++ .../rfc-2585-unsafe_op_in_unsafe_fn.stderr | 29 +++++++++++++++++++ 4 files changed, 82 insertions(+) create mode 100644 src/test/ui/feature-gates/feature-gate-unsafe_block_in_unsafe_fn.rs create mode 100644 src/test/ui/feature-gates/feature-gate-unsafe_block_in_unsafe_fn.stderr create mode 100644 src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.rs create mode 100644 src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.stderr diff --git a/src/test/ui/feature-gates/feature-gate-unsafe_block_in_unsafe_fn.rs b/src/test/ui/feature-gates/feature-gate-unsafe_block_in_unsafe_fn.rs new file mode 100644 index 000000000000..80005fd4ef63 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-unsafe_block_in_unsafe_fn.rs @@ -0,0 +1,11 @@ +#![deny(unused_unsafe)] + +unsafe fn unsf() {} + +unsafe fn foo() { + unsafe { //~ ERROR unnecessary `unsafe` block + unsf() + } +} + +fn main() {} diff --git a/src/test/ui/feature-gates/feature-gate-unsafe_block_in_unsafe_fn.stderr b/src/test/ui/feature-gates/feature-gate-unsafe_block_in_unsafe_fn.stderr new file mode 100644 index 000000000000..64874e590fb8 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-unsafe_block_in_unsafe_fn.stderr @@ -0,0 +1,16 @@ +error: unnecessary `unsafe` block + --> $DIR/feature-gate-unsafe_block_in_unsafe_fn.rs:6:5 + | +LL | unsafe fn foo() { + | --------------- because it's nested under this `unsafe` fn +LL | unsafe { + | ^^^^^^ unnecessary `unsafe` block + | +note: the lint level is defined here + --> $DIR/feature-gate-unsafe_block_in_unsafe_fn.rs:1:9 + | +LL | #![deny(unused_unsafe)] + | ^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.rs b/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.rs new file mode 100644 index 000000000000..0ffb2827bfde --- /dev/null +++ b/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.rs @@ -0,0 +1,26 @@ +#![feature(unsafe_block_in_unsafe_fn)] +#![warn(unsafe_op_in_unsafe_fn)] +#![deny(unused_unsafe)] + +unsafe fn unsf() {} + +unsafe fn foo() { + unsf(); + //~^ WARNING call to unsafe function is unsafe and requires unsafe block +} + +unsafe fn bar() { + unsafe { unsf() } // no error +} + +unsafe fn baz() { + unsafe { unsafe { unsf() } } + //~^ ERROR unnecessary `unsafe` block +} + +#[allow(unsafe_op_in_unsafe_fn)] +unsafe fn qux() { + unsf(); // no error +} + +fn main() {} diff --git a/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.stderr b/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.stderr new file mode 100644 index 000000000000..be6af2a11c40 --- /dev/null +++ b/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.stderr @@ -0,0 +1,29 @@ +warning: call to unsafe function is unsafe and requires unsafe block (error E0133) + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:8:5 + | +LL | unsf(); + | ^^^^^^ call to unsafe function + | +note: the lint level is defined here + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:2:9 + | +LL | #![warn(unsafe_op_in_unsafe_fn)] + | ^^^^^^^^^^^^^^^^^^^^^^ + = note: consult the function's documentation for information on how to avoid undefined behavior + +error: unnecessary `unsafe` block + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:17:14 + | +LL | unsafe { unsafe { unsf() } } + | ------ ^^^^^^ unnecessary `unsafe` block + | | + | because it's nested under this `unsafe` block + | +note: the lint level is defined here + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:3:9 + | +LL | #![deny(unused_unsafe)] + | ^^^^^^^^^^^^^ + +error: aborting due to previous error; 1 warning emitted + From bb6791502846b62e752c99396953e4db7739f28c Mon Sep 17 00:00:00 2001 From: LeSeulArtichaut Date: Wed, 13 May 2020 23:43:21 +0200 Subject: [PATCH 29/56] Apply suggestions from code review --- src/librustc_lint/levels.rs | 19 +++++- src/librustc_middle/mir/query.rs | 5 +- src/librustc_mir/transform/check_unsafety.rs | 67 ++++++++++++------- src/librustc_mir_build/build/block.rs | 12 +++- .../feature-gate-unsafe_block_in_unsafe_fn.rs | 11 +-- ...ture-gate-unsafe_block_in_unsafe_fn.stderr | 36 +++++++--- .../unsafe/rfc-2585-unsafe_op_in_unsafe_fn.rs | 8 ++- .../rfc-2585-unsafe_op_in_unsafe_fn.stderr | 17 ++++- 8 files changed, 125 insertions(+), 50 deletions(-) diff --git a/src/librustc_lint/levels.rs b/src/librustc_lint/levels.rs index 274e57ae64ca..706947201540 100644 --- a/src/librustc_lint/levels.rs +++ b/src/librustc_lint/levels.rs @@ -17,8 +17,8 @@ use rustc_middle::ty::TyCtxt; use rustc_session::lint::{builtin, Level, Lint}; use rustc_session::parse::feature_err; use rustc_session::Session; -use rustc_span::source_map::MultiSpan; use rustc_span::symbol::{sym, Symbol}; +use rustc_span::{source_map::MultiSpan, Span, DUMMY_SP}; use std::cmp; @@ -80,6 +80,8 @@ impl<'s> LintLevelsBuilder<'s> { let level = cmp::min(level, self.sets.lint_cap); let lint_flag_val = Symbol::intern(lint_name); + self.check_gated_lint(lint_flag_val, DUMMY_SP); + let ids = match store.find_lints(&lint_name) { Ok(ids) => ids, Err(_) => continue, // errors handled in check_lint_name_cmdline above @@ -211,6 +213,7 @@ impl<'s> LintLevelsBuilder<'s> { let name = meta_item.path.segments.last().expect("empty lint name").ident.name; match store.check_lint_name(&name.as_str(), tool_name) { CheckLintNameResult::Ok(ids) => { + self.check_gated_lint(name, attr.span); let src = LintSource::Node(name, li.span(), reason); for id in ids { specs.insert(*id, (level, src)); @@ -383,6 +386,20 @@ impl<'s> LintLevelsBuilder<'s> { BuilderPush { prev, changed: prev != self.cur } } + fn check_gated_lint(&self, name: Symbol, span: Span) { + if name.as_str() == "unsafe_op_in_unsafe_fn" + && !self.sess.features_untracked().unsafe_block_in_unsafe_fn + { + feature_err( + &self.sess.parse_sess, + sym::unsafe_block_in_unsafe_fn, + span, + "the `unsafe_op_in_unsafe_fn` lint is unstable", + ) + .emit(); + } + } + /// Called after `push` when the scope of a set of attributes are exited. pub fn pop(&mut self, push: BuilderPush) { self.cur = push.prev; diff --git a/src/librustc_middle/mir/query.rs b/src/librustc_middle/mir/query.rs index f19270e5561f..c63d7215867c 100644 --- a/src/librustc_middle/mir/query.rs +++ b/src/librustc_middle/mir/query.rs @@ -21,16 +21,17 @@ pub enum UnsafetyViolationKind { GeneralAndConstFn, /// Borrow of packed field. /// Has to be handled as a lint for backwards compatibility. - BorrowPacked(hir::HirId), + BorrowPacked, /// Unsafe operation in an `unsafe fn` but outside an `unsafe` block. /// Has to be handled as a lint for backwards compatibility. /// Should stay gated under `#![feature(unsafe_block_in_unsafe_fn)]`. - UnsafeFn(hir::HirId), + UnsafeFn, } #[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, HashStable)] pub struct UnsafetyViolation { pub source_info: SourceInfo, + pub lint_root: hir::HirId, pub description: Symbol, pub details: Symbol, pub kind: UnsafetyViolationKind, diff --git a/src/librustc_mir/transform/check_unsafety.rs b/src/librustc_mir/transform/check_unsafety.rs index bcadbd091076..0434f3447bce 100644 --- a/src/librustc_mir/transform/check_unsafety.rs +++ b/src/librustc_mir/transform/check_unsafety.rs @@ -2,6 +2,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::hir_id::HirId; use rustc_hir::intravisit; use rustc_hir::Node; use rustc_middle::mir::visit::{MutatingUseContext, PlaceContext, Visitor}; @@ -10,6 +11,7 @@ use rustc_middle::ty::cast::CastTy; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, TyCtxt}; use rustc_session::lint::builtin::{SAFE_PACKED_BORROWS, UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE}; +use rustc_session::lint::Level; use rustc_span::symbol::{sym, Symbol}; use std::ops::Bound; @@ -220,7 +222,18 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { for (i, elem) in place.projection.iter().enumerate() { let proj_base = &place.projection[..i]; - let old_source_info = self.source_info; + if context.is_borrow() { + if util::is_disaligned(self.tcx, self.body, self.param_env, *place) { + self.require_unsafe( + "borrow of packed field", + "fields of packed structs might be misaligned: dereferencing a \ + misaligned pointer or even just creating a misaligned reference \ + is undefined behavior", + UnsafetyViolationKind::BorrowPacked, + ); + } + } + let source_info = self.source_info; if let [] = proj_base { let decl = &self.body.local_decls[place.local]; if decl.internal { @@ -301,7 +314,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { } _ => {} } - self.source_info = old_source_info; + self.source_info = source_info; } } } @@ -314,9 +327,15 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> { kind: UnsafetyViolationKind, ) { let source_info = self.source_info; + let lint_root = self.body.source_scopes[self.source_info.scope] + .local_data + .as_ref() + .assert_crate_local() + .lint_root; self.register_violations( &[UnsafetyViolation { source_info, + lint_root, description: Symbol::intern(description), details: Symbol::intern(details), kind, @@ -343,7 +362,7 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> { match violation.kind { UnsafetyViolationKind::GeneralAndConstFn | UnsafetyViolationKind::General => {} - UnsafetyViolationKind::BorrowPacked(_) => { + UnsafetyViolationKind::BorrowPacked => { if self.min_const_fn { // const fns don't need to be backwards compatible and can // emit these violations as a hard error instead of a backwards @@ -351,7 +370,7 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> { violation.kind = UnsafetyViolationKind::General; } } - UnsafetyViolationKind::UnsafeFn(_) => { + UnsafetyViolationKind::UnsafeFn => { bug!("`UnsafetyViolationKind::UnsafeFn` in an `Safe` context") } } @@ -365,14 +384,9 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> { Safety::FnUnsafe if self.tcx.features().unsafe_block_in_unsafe_fn => { for violation in violations { let mut violation = *violation; - let lint_root = self.body.source_scopes[self.source_info.scope] - .local_data - .as_ref() - .assert_crate_local() - .lint_root; // FIXME(LeSeulArtichaut): what to do with `UnsafetyViolationKind::BorrowPacked`? - violation.kind = UnsafetyViolationKind::UnsafeFn(lint_root); + violation.kind = UnsafetyViolationKind::UnsafeFn; if !self.violations.contains(&violation) { self.violations.push(violation) } @@ -394,7 +408,7 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> { UnsafetyViolationKind::GeneralAndConstFn => {} // these things are forbidden in const fns UnsafetyViolationKind::General - | UnsafetyViolationKind::BorrowPacked(_) => { + | UnsafetyViolationKind::BorrowPacked => { let mut violation = *violation; // const fns don't need to be backwards compatible and can // emit these violations as a hard error instead of a backwards @@ -404,7 +418,7 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> { self.violations.push(violation) } } - UnsafetyViolationKind::UnsafeFn(_) => bug!( + UnsafetyViolationKind::UnsafeFn => bug!( "`UnsafetyViolationKind::UnsafeFn` in an `ExplicitUnsafe` context" ), } @@ -657,10 +671,13 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: DefId) { let UnsafetyCheckResult { violations, unsafe_blocks } = tcx.unsafety_check_result(def_id.expect_local()); - let or_block_msg = if tcx.features().unsafe_block_in_unsafe_fn { "" } else { " or block" }; - - for &UnsafetyViolation { source_info, description, details, kind } in violations.iter() { + for &UnsafetyViolation { source_info, lint_root, description, details, kind } in + violations.iter() + { // Report an error. + let unsafe_fn_msg = + if unsafe_op_in_unsafe_fn_allowed(tcx, lint_root) { "" } else { " function or" }; + match kind { UnsafetyViolationKind::GeneralAndConstFn | UnsafetyViolationKind::General => { // once @@ -668,26 +685,26 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: DefId) { tcx.sess, source_info.span, E0133, - "{} is unsafe and requires unsafe function{}", + "{} is unsafe and requires unsafe{} block", description, - or_block_msg, + unsafe_fn_msg, ) .span_label(source_info.span, &*description.as_str()) .note(&details.as_str()) .emit(); } - UnsafetyViolationKind::BorrowPacked(lint_hir_id) => { + UnsafetyViolationKind::BorrowPacked => { if let Some(impl_def_id) = builtin_derive_def_id(tcx, def_id) { tcx.ensure().unsafe_derive_on_repr_packed(impl_def_id); } else { tcx.struct_span_lint_hir( SAFE_PACKED_BORROWS, - lint_hir_id, + lint_root, source_info.span, |lint| { lint.build(&format!( - "{} is unsafe and requires unsafe function{} (error E0133)", - description, or_block_msg, + "{} is unsafe and requires unsafe{} block (error E0133)", + description, unsafe_fn_msg, )) .note(&details.as_str()) .emit() @@ -695,9 +712,9 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: DefId) { ) } } - UnsafetyViolationKind::UnsafeFn(lint_hir_id) => tcx.struct_span_lint_hir( + UnsafetyViolationKind::UnsafeFn => tcx.struct_span_lint_hir( UNSAFE_OP_IN_UNSAFE_FN, - lint_hir_id, + lint_root, source_info.span, |lint| { lint.build(&format!( @@ -728,3 +745,7 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: DefId) { report_unused_unsafe(tcx, &unsafe_used, block_id); } } + +fn unsafe_op_in_unsafe_fn_allowed(tcx: TyCtxt<'_>, id: HirId) -> bool { + tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, id).0 == Level::Allow +} diff --git a/src/librustc_mir_build/build/block.rs b/src/librustc_mir_build/build/block.rs index b052c839152d..4e4f0dc74cb7 100644 --- a/src/librustc_mir_build/build/block.rs +++ b/src/librustc_mir_build/build/block.rs @@ -4,6 +4,8 @@ use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder}; use crate::hair::*; use rustc_hir as hir; use rustc_middle::mir::*; +use rustc_session::lint::builtin::UNSAFE_OP_IN_UNSAFE_FN; +use rustc_session::lint::Level; use rustc_span::Span; impl<'a, 'tcx> Builder<'a, 'tcx> { @@ -218,7 +220,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { match self.unpushed_unsafe { Safety::Safe => {} // no longer treat `unsafe fn`s as `unsafe` contexts (see RFC #2585) - Safety::FnUnsafe if self.hir.tcx().features().unsafe_block_in_unsafe_fn => {} + Safety::FnUnsafe + if self.hir.tcx().lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, hir_id).0 + != Level::Allow => {} _ => return, } self.unpushed_unsafe = Safety::ExplicitUnsafe(hir_id); @@ -233,7 +237,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { .push_unsafe_count .checked_sub(1) .unwrap_or_else(|| span_bug!(span, "unsafe count underflow")); - if self.push_unsafe_count == 0 { Some(self.unpushed_unsafe) } else { None } + if self.push_unsafe_count == 0 { + Some(self.unpushed_unsafe) + } else { + None + } } }; diff --git a/src/test/ui/feature-gates/feature-gate-unsafe_block_in_unsafe_fn.rs b/src/test/ui/feature-gates/feature-gate-unsafe_block_in_unsafe_fn.rs index 80005fd4ef63..bf33ac67fcae 100644 --- a/src/test/ui/feature-gates/feature-gate-unsafe_block_in_unsafe_fn.rs +++ b/src/test/ui/feature-gates/feature-gate-unsafe_block_in_unsafe_fn.rs @@ -1,11 +1,4 @@ -#![deny(unused_unsafe)] - -unsafe fn unsf() {} - -unsafe fn foo() { - unsafe { //~ ERROR unnecessary `unsafe` block - unsf() - } -} +#![deny(unsafe_op_in_unsafe_fn)] +//~^ ERROR the `unsafe_op_in_unsafe_fn` lint is unstable fn main() {} diff --git a/src/test/ui/feature-gates/feature-gate-unsafe_block_in_unsafe_fn.stderr b/src/test/ui/feature-gates/feature-gate-unsafe_block_in_unsafe_fn.stderr index 64874e590fb8..c5cad4a98d9c 100644 --- a/src/test/ui/feature-gates/feature-gate-unsafe_block_in_unsafe_fn.stderr +++ b/src/test/ui/feature-gates/feature-gate-unsafe_block_in_unsafe_fn.stderr @@ -1,16 +1,30 @@ -error: unnecessary `unsafe` block - --> $DIR/feature-gate-unsafe_block_in_unsafe_fn.rs:6:5 +error[E0658]: the `unsafe_op_in_unsafe_fn` lint is unstable + --> $DIR/feature-gate-unsafe_block_in_unsafe_fn.rs:1:1 | -LL | unsafe fn foo() { - | --------------- because it's nested under this `unsafe` fn -LL | unsafe { - | ^^^^^^ unnecessary `unsafe` block +LL | #![deny(unsafe_op_in_unsafe_fn)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: the lint level is defined here - --> $DIR/feature-gate-unsafe_block_in_unsafe_fn.rs:1:9 + = note: see issue #71668 for more information + = help: add `#![feature(unsafe_block_in_unsafe_fn)]` to the crate attributes to enable + +error[E0658]: the `unsafe_op_in_unsafe_fn` lint is unstable + --> $DIR/feature-gate-unsafe_block_in_unsafe_fn.rs:1:1 + | +LL | #![deny(unsafe_op_in_unsafe_fn)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #71668 for more information + = help: add `#![feature(unsafe_block_in_unsafe_fn)]` to the crate attributes to enable + +error[E0658]: the `unsafe_op_in_unsafe_fn` lint is unstable + --> $DIR/feature-gate-unsafe_block_in_unsafe_fn.rs:1:1 + | +LL | #![deny(unsafe_op_in_unsafe_fn)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -LL | #![deny(unused_unsafe)] - | ^^^^^^^^^^^^^ + = note: see issue #71668 for more information + = help: add `#![feature(unsafe_block_in_unsafe_fn)]` to the crate attributes to enable -error: aborting due to previous error +error: aborting due to 3 previous errors +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.rs b/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.rs index 0ffb2827bfde..7feb68b98577 100644 --- a/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.rs +++ b/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.rs @@ -21,6 +21,12 @@ unsafe fn baz() { #[allow(unsafe_op_in_unsafe_fn)] unsafe fn qux() { unsf(); // no error + + unsafe { unsf() } + //~^ ERROR unnecessary `unsafe` block } -fn main() {} +fn main() { + unsf() + //~^ ERROR call to unsafe function is unsafe and requires unsafe function or block +} diff --git a/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.stderr b/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.stderr index be6af2a11c40..e812210498c4 100644 --- a/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.stderr +++ b/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.stderr @@ -25,5 +25,20 @@ note: the lint level is defined here LL | #![deny(unused_unsafe)] | ^^^^^^^^^^^^^ -error: aborting due to previous error; 1 warning emitted +error: unnecessary `unsafe` block + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:25:5 + | +LL | unsafe { unsf() } + | ^^^^^^ unnecessary `unsafe` block + +error[E0133]: call to unsafe function is unsafe and requires unsafe function or block + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:30:5 + | +LL | unsf() + | ^^^^^^ call to unsafe function + | + = note: consult the function's documentation for information on how to avoid undefined behavior + +error: aborting due to 3 previous errors; 1 warning emitted +For more information about this error, try `rustc --explain E0133`. From 3ce9d5c26982ff0516946dda7fa8d29580fa25b3 Mon Sep 17 00:00:00 2001 From: LeSeulArtichaut Date: Thu, 14 May 2020 20:37:23 +0200 Subject: [PATCH 30/56] Add more cases to the test --- .../unsafe/rfc-2585-unsafe_op_in_unsafe_fn.rs | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.rs b/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.rs index 7feb68b98577..618c91158159 100644 --- a/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.rs +++ b/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.rs @@ -1,16 +1,38 @@ #![feature(unsafe_block_in_unsafe_fn)] #![warn(unsafe_op_in_unsafe_fn)] #![deny(unused_unsafe)] +#![deny(safe_packed_borrows)] unsafe fn unsf() {} +const PTR: *const () = std::ptr::null(); +static mut VOID: () = (); + +#[repr(packed)] +pub struct Packed { + data: &'static u32, +} + +const PACKED: Packed = Packed { data: &0 }; unsafe fn foo() { unsf(); //~^ WARNING call to unsafe function is unsafe and requires unsafe block + *PTR; + //~^ WARNING dereference of raw pointer is unsafe and requires unsafe block + VOID = (); + //~^ WARNING use of mutable static is unsafe and requires unsafe block + &PACKED.data; // the level for the `safe_packed_borrows` lint is ignored + //~^ WARNING borrow of packed field is unsafe and requires unsafe block } unsafe fn bar() { - unsafe { unsf() } // no error + // no error + unsafe { + unsf(); + *PTR; + VOID = (); + &PACKED.data; + } } unsafe fn baz() { @@ -20,7 +42,11 @@ unsafe fn baz() { #[allow(unsafe_op_in_unsafe_fn)] unsafe fn qux() { - unsf(); // no error + // lint allowed -> no error + unsf(); + *PTR; + VOID = (); + &PACKED.data; unsafe { unsf() } //~^ ERROR unnecessary `unsafe` block From b3e012becc6539de9570eee6ce694f07879935d2 Mon Sep 17 00:00:00 2001 From: LeSeulArtichaut Date: Tue, 19 May 2020 00:18:50 +0200 Subject: [PATCH 31/56] Fix inverted `if` condition --- src/librustc_mir/transform/check_unsafety.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_mir/transform/check_unsafety.rs b/src/librustc_mir/transform/check_unsafety.rs index 0434f3447bce..3cff214f441c 100644 --- a/src/librustc_mir/transform/check_unsafety.rs +++ b/src/librustc_mir/transform/check_unsafety.rs @@ -676,7 +676,7 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: DefId) { { // Report an error. let unsafe_fn_msg = - if unsafe_op_in_unsafe_fn_allowed(tcx, lint_root) { "" } else { " function or" }; + if unsafe_op_in_unsafe_fn_allowed(tcx, lint_root) { " function or" } else { "" }; match kind { UnsafetyViolationKind::GeneralAndConstFn | UnsafetyViolationKind::General => { From a41f76321a05ce7941d843dc18ea11f7c8a11c6f Mon Sep 17 00:00:00 2001 From: LeSeulArtichaut Date: Tue, 19 May 2020 00:19:31 +0200 Subject: [PATCH 32/56] Use the lowest of `unsafe_op_in_unsafe_fn` and `safe_borrow_packed` for packed borrows in unsafe fns --- src/librustc_middle/mir/query.rs | 4 +++ src/librustc_mir/transform/check_unsafety.rs | 33 +++++++++++++++++--- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/librustc_middle/mir/query.rs b/src/librustc_middle/mir/query.rs index c63d7215867c..99bfb74c243b 100644 --- a/src/librustc_middle/mir/query.rs +++ b/src/librustc_middle/mir/query.rs @@ -26,6 +26,10 @@ pub enum UnsafetyViolationKind { /// Has to be handled as a lint for backwards compatibility. /// Should stay gated under `#![feature(unsafe_block_in_unsafe_fn)]`. UnsafeFn, + /// Borrow of packed field in an `unsafe fn` but outside an `unsafe` block. + /// Has to be handled as a lint for backwards compatibility. + /// Should stay gated under `#![feature(unsafe_block_in_unsafe_fn)]`. + UnsafeFnBorrowPacked, } #[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, HashStable)] diff --git a/src/librustc_mir/transform/check_unsafety.rs b/src/librustc_mir/transform/check_unsafety.rs index 3cff214f441c..177efa2fc0d6 100644 --- a/src/librustc_mir/transform/check_unsafety.rs +++ b/src/librustc_mir/transform/check_unsafety.rs @@ -370,7 +370,8 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> { violation.kind = UnsafetyViolationKind::General; } } - UnsafetyViolationKind::UnsafeFn => { + UnsafetyViolationKind::UnsafeFn + | UnsafetyViolationKind::UnsafeFnBorrowPacked => { bug!("`UnsafetyViolationKind::UnsafeFn` in an `Safe` context") } } @@ -385,8 +386,11 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> { for violation in violations { let mut violation = *violation; - // FIXME(LeSeulArtichaut): what to do with `UnsafetyViolationKind::BorrowPacked`? - violation.kind = UnsafetyViolationKind::UnsafeFn; + if violation.kind == UnsafetyViolationKind::BorrowPacked { + violation.kind = UnsafetyViolationKind::UnsafeFnBorrowPacked; + } else { + violation.kind = UnsafetyViolationKind::UnsafeFn; + } if !self.violations.contains(&violation) { self.violations.push(violation) } @@ -418,7 +422,8 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> { self.violations.push(violation) } } - UnsafetyViolationKind::UnsafeFn => bug!( + UnsafetyViolationKind::UnsafeFn + | UnsafetyViolationKind::UnsafeFnBorrowPacked => bug!( "`UnsafetyViolationKind::UnsafeFn` in an `ExplicitUnsafe` context" ), } @@ -719,13 +724,31 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: DefId) { |lint| { lint.build(&format!( "{} is unsafe and requires unsafe block (error E0133)", - description + description, )) .span_label(source_info.span, &*description.as_str()) .note(&details.as_str()) .emit(); }, ), + UnsafetyViolationKind::UnsafeFnBorrowPacked => { + let lint = if tcx.lint_level_at_node(SAFE_PACKED_BORROWS, lint_root).0 + <= tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, lint_root).0 + { + SAFE_PACKED_BORROWS + } else { + UNSAFE_OP_IN_UNSAFE_FN + }; + tcx.struct_span_lint_hir(&lint, lint_root, source_info.span, |lint| { + lint.build(&format!( + "{} is unsafe and requires unsafe block (error E0133)", + description, + )) + .span_label(source_info.span, &*description.as_str()) + .note(&details.as_str()) + .emit(); + }) + } } } From a3bae5ce73a65671ee35037ac988766973628295 Mon Sep 17 00:00:00 2001 From: LeSeulArtichaut Date: Tue, 19 May 2020 12:16:40 +0200 Subject: [PATCH 33/56] Fix wrong conflict resolution --- src/librustc_mir/transform/check_unsafety.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/librustc_mir/transform/check_unsafety.rs b/src/librustc_mir/transform/check_unsafety.rs index 177efa2fc0d6..7be640ae2431 100644 --- a/src/librustc_mir/transform/check_unsafety.rs +++ b/src/librustc_mir/transform/check_unsafety.rs @@ -204,18 +204,12 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { if context.is_borrow() { if util::is_disaligned(self.tcx, self.body, self.param_env, *place) { - let source_info = self.source_info; - let lint_root = self.body.source_scopes[source_info.scope] - .local_data - .as_ref() - .assert_crate_local() - .lint_root; self.require_unsafe( "borrow of packed field", "fields of packed structs might be misaligned: dereferencing a \ misaligned pointer or even just creating a misaligned reference \ is undefined behavior", - UnsafetyViolationKind::BorrowPacked(lint_root), + UnsafetyViolationKind::BorrowPacked, ); } } From 925d5ac45f4a44dd61f1ebd4b7bd3cc89fcb2b5f Mon Sep 17 00:00:00 2001 From: LeSeulArtichaut Date: Thu, 21 May 2020 23:53:12 +0200 Subject: [PATCH 34/56] Fix and bless tests --- .../unsafe/rfc-2585-unsafe_op_in_unsafe_fn.rs | 71 +++++++++-- .../rfc-2585-unsafe_op_in_unsafe_fn.stderr | 118 ++++++++++++++++-- 2 files changed, 168 insertions(+), 21 deletions(-) diff --git a/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.rs b/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.rs index 618c91158159..6dfb97c230d6 100644 --- a/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.rs +++ b/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.rs @@ -1,5 +1,5 @@ #![feature(unsafe_block_in_unsafe_fn)] -#![warn(unsafe_op_in_unsafe_fn)] +#![deny(unsafe_op_in_unsafe_fn)] #![deny(unused_unsafe)] #![deny(safe_packed_borrows)] @@ -14,18 +14,34 @@ pub struct Packed { const PACKED: Packed = Packed { data: &0 }; -unsafe fn foo() { +unsafe fn deny_level() { unsf(); - //~^ WARNING call to unsafe function is unsafe and requires unsafe block + //~^ ERROR call to unsafe function is unsafe and requires unsafe block *PTR; - //~^ WARNING dereference of raw pointer is unsafe and requires unsafe block + //~^ ERROR dereference of raw pointer is unsafe and requires unsafe block VOID = (); - //~^ WARNING use of mutable static is unsafe and requires unsafe block - &PACKED.data; // the level for the `safe_packed_borrows` lint is ignored - //~^ WARNING borrow of packed field is unsafe and requires unsafe block + //~^ ERROR use of mutable static is unsafe and requires unsafe block + &PACKED.data; + //~^ ERROR borrow of packed field is unsafe and requires unsafe block + //~| WARNING this was previously accepted by the compiler but is being phased out } -unsafe fn bar() { +// Check that `unsafe_op_in_unsafe_fn` works starting from the `warn` level. +#[warn(unsafe_op_in_unsafe_fn)] +#[deny(warnings)] +unsafe fn warning_level() { + unsf(); + //~^ ERROR call to unsafe function is unsafe and requires unsafe block + *PTR; + //~^ ERROR dereference of raw pointer is unsafe and requires unsafe block + VOID = (); + //~^ ERROR use of mutable static is unsafe and requires unsafe block + &PACKED.data; + //~^ ERROR borrow of packed field is unsafe and requires unsafe block + //~| WARNING this was previously accepted by the compiler but is being phased out +} + +unsafe fn explicit_block() { // no error unsafe { unsf(); @@ -35,13 +51,25 @@ unsafe fn bar() { } } -unsafe fn baz() { +unsafe fn two_explicit_blocks() { unsafe { unsafe { unsf() } } //~^ ERROR unnecessary `unsafe` block } +#[warn(safe_packed_borrows)] +unsafe fn warn_packed_borrows() { + &PACKED.data; + //~^ WARNING borrow of packed field is unsafe and requires unsafe block + //~| WARNING this was previously accepted by the compiler but is being phased out +} + +#[allow(safe_packed_borrows)] +unsafe fn allow_packed_borrows() { + &PACKED.data; // `safe_packed_borrows` is allowed, no error +} + #[allow(unsafe_op_in_unsafe_fn)] -unsafe fn qux() { +unsafe fn allow_level() { // lint allowed -> no error unsf(); *PTR; @@ -52,7 +80,26 @@ unsafe fn qux() { //~^ ERROR unnecessary `unsafe` block } +unsafe fn nested_allow_level() { + #[allow(unsafe_op_in_unsafe_fn)] + { + // lint allowed -> no error + unsf(); + *PTR; + VOID = (); + &PACKED.data; + + unsafe { unsf() } + //~^ ERROR unnecessary `unsafe` block + } +} + fn main() { - unsf() - //~^ ERROR call to unsafe function is unsafe and requires unsafe function or block + unsf(); + //~^ ERROR call to unsafe function is unsafe and requires unsafe block + #[allow(unsafe_op_in_unsafe_fn)] + { + unsf(); + //~^ ERROR call to unsafe function is unsafe and requires unsafe function or block + } } diff --git a/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.stderr b/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.stderr index e812210498c4..efc53f0b28b2 100644 --- a/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.stderr +++ b/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.stderr @@ -1,5 +1,5 @@ -warning: call to unsafe function is unsafe and requires unsafe block (error E0133) - --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:8:5 +error: call to unsafe function is unsafe and requires unsafe block (error E0133) + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:18:5 | LL | unsf(); | ^^^^^^ call to unsafe function @@ -7,12 +7,83 @@ LL | unsf(); note: the lint level is defined here --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:2:9 | -LL | #![warn(unsafe_op_in_unsafe_fn)] +LL | #![deny(unsafe_op_in_unsafe_fn)] | ^^^^^^^^^^^^^^^^^^^^^^ = note: consult the function's documentation for information on how to avoid undefined behavior +error: dereference of raw pointer is unsafe and requires unsafe block (error E0133) + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:20:5 + | +LL | *PTR; + | ^^^^ dereference of raw pointer + | + = note: raw pointers may be NULL, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior + +error: use of mutable static is unsafe and requires unsafe block (error E0133) + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:22:5 + | +LL | VOID = (); + | ^^^^^^^^^ use of mutable static + | + = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior + +error: borrow of packed field is unsafe and requires unsafe block (error E0133) + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:24:5 + | +LL | &PACKED.data; + | ^^^^^^^^^^^^ borrow of packed field + | +note: the lint level is defined here + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:4:9 + | +LL | #![deny(safe_packed_borrows)] + | ^^^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #46043 + = note: fields of packed structs might be misaligned: dereferencing a misaligned pointer or even just creating a misaligned reference is undefined behavior + +error: call to unsafe function is unsafe and requires unsafe block (error E0133) + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:33:5 + | +LL | unsf(); + | ^^^^^^ call to unsafe function + | +note: the lint level is defined here + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:31:8 + | +LL | #[deny(warnings)] + | ^^^^^^^^ + = note: `#[deny(unsafe_op_in_unsafe_fn)]` implied by `#[deny(warnings)]` + = note: consult the function's documentation for information on how to avoid undefined behavior + +error: dereference of raw pointer is unsafe and requires unsafe block (error E0133) + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:35:5 + | +LL | *PTR; + | ^^^^ dereference of raw pointer + | + = note: raw pointers may be NULL, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior + +error: use of mutable static is unsafe and requires unsafe block (error E0133) + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:37:5 + | +LL | VOID = (); + | ^^^^^^^^^ use of mutable static + | + = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior + +error: borrow of packed field is unsafe and requires unsafe block (error E0133) + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:39:5 + | +LL | &PACKED.data; + | ^^^^^^^^^^^^ borrow of packed field + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #46043 + = note: fields of packed structs might be misaligned: dereferencing a misaligned pointer or even just creating a misaligned reference is undefined behavior + error: unnecessary `unsafe` block - --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:17:14 + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:55:14 | LL | unsafe { unsafe { unsf() } } | ------ ^^^^^^ unnecessary `unsafe` block @@ -25,20 +96,49 @@ note: the lint level is defined here LL | #![deny(unused_unsafe)] | ^^^^^^^^^^^^^ +warning: borrow of packed field is unsafe and requires unsafe block (error E0133) + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:61:5 + | +LL | &PACKED.data; + | ^^^^^^^^^^^^ borrow of packed field + | +note: the lint level is defined here + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:59:8 + | +LL | #[warn(safe_packed_borrows)] + | ^^^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #46043 + = note: fields of packed structs might be misaligned: dereferencing a misaligned pointer or even just creating a misaligned reference is undefined behavior + error: unnecessary `unsafe` block - --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:25:5 + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:79:5 | LL | unsafe { unsf() } | ^^^^^^ unnecessary `unsafe` block -error[E0133]: call to unsafe function is unsafe and requires unsafe function or block - --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:30:5 +error: unnecessary `unsafe` block + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:92:9 | -LL | unsf() +LL | unsafe { unsf() } + | ^^^^^^ unnecessary `unsafe` block + +error[E0133]: call to unsafe function is unsafe and requires unsafe block + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:98:5 + | +LL | unsf(); | ^^^^^^ call to unsafe function | = note: consult the function's documentation for information on how to avoid undefined behavior -error: aborting due to 3 previous errors; 1 warning emitted +error[E0133]: call to unsafe function is unsafe and requires unsafe function or block + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:102:9 + | +LL | unsf(); + | ^^^^^^ call to unsafe function + | + = note: consult the function's documentation for information on how to avoid undefined behavior + +error: aborting due to 13 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0133`. From 9671b44609d2adbf041fcc5f8b63183786417ba7 Mon Sep 17 00:00:00 2001 From: LeSeulArtichaut Date: Fri, 22 May 2020 20:21:26 +0200 Subject: [PATCH 35/56] Add tests for packed borrows in unsafe fns --- ...c-2585-safe_packed_borrows-in-unsafe-fn.rs | 67 +++++++++++++++++++ ...85-safe_packed_borrows-in-unsafe-fn.stderr | 60 +++++++++++++++++ .../unsafe/rfc-2585-unsafe_op_in_unsafe_fn.rs | 29 -------- .../rfc-2585-unsafe_op_in_unsafe_fn.stderr | 66 ++++-------------- 4 files changed, 140 insertions(+), 82 deletions(-) create mode 100644 src/test/ui/unsafe/rfc-2585-safe_packed_borrows-in-unsafe-fn.rs create mode 100644 src/test/ui/unsafe/rfc-2585-safe_packed_borrows-in-unsafe-fn.stderr diff --git a/src/test/ui/unsafe/rfc-2585-safe_packed_borrows-in-unsafe-fn.rs b/src/test/ui/unsafe/rfc-2585-safe_packed_borrows-in-unsafe-fn.rs new file mode 100644 index 000000000000..540612a7dce0 --- /dev/null +++ b/src/test/ui/unsafe/rfc-2585-safe_packed_borrows-in-unsafe-fn.rs @@ -0,0 +1,67 @@ +#![feature(unsafe_block_in_unsafe_fn)] + +#[repr(packed)] +pub struct Packed { + data: &'static u32, +} + +const PACKED: Packed = Packed { data: &0 }; + +#[allow(safe_packed_borrows)] +#[allow(unsafe_op_in_unsafe_fn)] +unsafe fn allow_allow() { + &PACKED.data; // allowed +} + +#[allow(safe_packed_borrows)] +#[warn(unsafe_op_in_unsafe_fn)] +unsafe fn allow_warn() { + &PACKED.data; // allowed +} + +#[allow(safe_packed_borrows)] +#[deny(unsafe_op_in_unsafe_fn)] +unsafe fn allow_deny() { + &PACKED.data; // allowed +} + +#[warn(safe_packed_borrows)] +#[allow(unsafe_op_in_unsafe_fn)] +unsafe fn warn_allow() { + &PACKED.data; // allowed +} + +#[warn(safe_packed_borrows)] +#[warn(unsafe_op_in_unsafe_fn)] +unsafe fn warn_warn() { + &PACKED.data; //~ WARN + //~| WARNING this was previously accepted by the compiler but is being phased out +} + +#[warn(safe_packed_borrows)] +#[deny(unsafe_op_in_unsafe_fn)] +unsafe fn warn_deny() { + &PACKED.data; //~ WARN + //~| WARNING this was previously accepted by the compiler but is being phased out +} + +#[deny(safe_packed_borrows)] +#[allow(unsafe_op_in_unsafe_fn)] +unsafe fn deny_allow() { + &PACKED.data; // allowed +} + +#[deny(safe_packed_borrows)] +#[warn(unsafe_op_in_unsafe_fn)] +unsafe fn deny_warn() { + &PACKED.data; //~ WARN +} + +#[deny(safe_packed_borrows)] +#[deny(unsafe_op_in_unsafe_fn)] +unsafe fn deny_deny() { + &PACKED.data; //~ ERROR + //~| WARNING this was previously accepted by the compiler but is being phased out +} + +fn main() {} diff --git a/src/test/ui/unsafe/rfc-2585-safe_packed_borrows-in-unsafe-fn.stderr b/src/test/ui/unsafe/rfc-2585-safe_packed_borrows-in-unsafe-fn.stderr new file mode 100644 index 000000000000..fda15159643b --- /dev/null +++ b/src/test/ui/unsafe/rfc-2585-safe_packed_borrows-in-unsafe-fn.stderr @@ -0,0 +1,60 @@ +warning: borrow of packed field is unsafe and requires unsafe block (error E0133) + --> $DIR/rfc-2585-safe_packed_borrows-in-unsafe-fn.rs:37:5 + | +LL | &PACKED.data; + | ^^^^^^^^^^^^ borrow of packed field + | +note: the lint level is defined here + --> $DIR/rfc-2585-safe_packed_borrows-in-unsafe-fn.rs:34:8 + | +LL | #[warn(safe_packed_borrows)] + | ^^^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #46043 + = note: fields of packed structs might be misaligned: dereferencing a misaligned pointer or even just creating a misaligned reference is undefined behavior + +warning: borrow of packed field is unsafe and requires unsafe block (error E0133) + --> $DIR/rfc-2585-safe_packed_borrows-in-unsafe-fn.rs:44:5 + | +LL | &PACKED.data; + | ^^^^^^^^^^^^ borrow of packed field + | +note: the lint level is defined here + --> $DIR/rfc-2585-safe_packed_borrows-in-unsafe-fn.rs:41:8 + | +LL | #[warn(safe_packed_borrows)] + | ^^^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #46043 + = note: fields of packed structs might be misaligned: dereferencing a misaligned pointer or even just creating a misaligned reference is undefined behavior + +warning: borrow of packed field is unsafe and requires unsafe block (error E0133) + --> $DIR/rfc-2585-safe_packed_borrows-in-unsafe-fn.rs:57:5 + | +LL | &PACKED.data; + | ^^^^^^^^^^^^ borrow of packed field + | +note: the lint level is defined here + --> $DIR/rfc-2585-safe_packed_borrows-in-unsafe-fn.rs:55:8 + | +LL | #[warn(unsafe_op_in_unsafe_fn)] + | ^^^^^^^^^^^^^^^^^^^^^^ + = note: fields of packed structs might be misaligned: dereferencing a misaligned pointer or even just creating a misaligned reference is undefined behavior + +error: borrow of packed field is unsafe and requires unsafe block (error E0133) + --> $DIR/rfc-2585-safe_packed_borrows-in-unsafe-fn.rs:63:5 + | +LL | &PACKED.data; + | ^^^^^^^^^^^^ borrow of packed field + | +note: the lint level is defined here + --> $DIR/rfc-2585-safe_packed_borrows-in-unsafe-fn.rs:60:8 + | +LL | #[deny(safe_packed_borrows)] + | ^^^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #46043 + = note: fields of packed structs might be misaligned: dereferencing a misaligned pointer or even just creating a misaligned reference is undefined behavior + +error: aborting due to previous error; 3 warnings emitted + diff --git a/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.rs b/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.rs index 6dfb97c230d6..1e57b03ced48 100644 --- a/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.rs +++ b/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.rs @@ -1,19 +1,11 @@ #![feature(unsafe_block_in_unsafe_fn)] #![deny(unsafe_op_in_unsafe_fn)] #![deny(unused_unsafe)] -#![deny(safe_packed_borrows)] unsafe fn unsf() {} const PTR: *const () = std::ptr::null(); static mut VOID: () = (); -#[repr(packed)] -pub struct Packed { - data: &'static u32, -} - -const PACKED: Packed = Packed { data: &0 }; - unsafe fn deny_level() { unsf(); //~^ ERROR call to unsafe function is unsafe and requires unsafe block @@ -21,9 +13,6 @@ unsafe fn deny_level() { //~^ ERROR dereference of raw pointer is unsafe and requires unsafe block VOID = (); //~^ ERROR use of mutable static is unsafe and requires unsafe block - &PACKED.data; - //~^ ERROR borrow of packed field is unsafe and requires unsafe block - //~| WARNING this was previously accepted by the compiler but is being phased out } // Check that `unsafe_op_in_unsafe_fn` works starting from the `warn` level. @@ -36,9 +25,6 @@ unsafe fn warning_level() { //~^ ERROR dereference of raw pointer is unsafe and requires unsafe block VOID = (); //~^ ERROR use of mutable static is unsafe and requires unsafe block - &PACKED.data; - //~^ ERROR borrow of packed field is unsafe and requires unsafe block - //~| WARNING this was previously accepted by the compiler but is being phased out } unsafe fn explicit_block() { @@ -47,7 +33,6 @@ unsafe fn explicit_block() { unsf(); *PTR; VOID = (); - &PACKED.data; } } @@ -56,25 +41,12 @@ unsafe fn two_explicit_blocks() { //~^ ERROR unnecessary `unsafe` block } -#[warn(safe_packed_borrows)] -unsafe fn warn_packed_borrows() { - &PACKED.data; - //~^ WARNING borrow of packed field is unsafe and requires unsafe block - //~| WARNING this was previously accepted by the compiler but is being phased out -} - -#[allow(safe_packed_borrows)] -unsafe fn allow_packed_borrows() { - &PACKED.data; // `safe_packed_borrows` is allowed, no error -} - #[allow(unsafe_op_in_unsafe_fn)] unsafe fn allow_level() { // lint allowed -> no error unsf(); *PTR; VOID = (); - &PACKED.data; unsafe { unsf() } //~^ ERROR unnecessary `unsafe` block @@ -87,7 +59,6 @@ unsafe fn nested_allow_level() { unsf(); *PTR; VOID = (); - &PACKED.data; unsafe { unsf() } //~^ ERROR unnecessary `unsafe` block diff --git a/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.stderr b/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.stderr index efc53f0b28b2..cc595df12cc4 100644 --- a/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.stderr +++ b/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.stderr @@ -1,5 +1,5 @@ error: call to unsafe function is unsafe and requires unsafe block (error E0133) - --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:18:5 + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:10:5 | LL | unsf(); | ^^^^^^ call to unsafe function @@ -12,7 +12,7 @@ LL | #![deny(unsafe_op_in_unsafe_fn)] = note: consult the function's documentation for information on how to avoid undefined behavior error: dereference of raw pointer is unsafe and requires unsafe block (error E0133) - --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:20:5 + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:12:5 | LL | *PTR; | ^^^^ dereference of raw pointer @@ -20,36 +20,21 @@ LL | *PTR; = note: raw pointers may be NULL, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior error: use of mutable static is unsafe and requires unsafe block (error E0133) - --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:22:5 + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:14:5 | LL | VOID = (); | ^^^^^^^^^ use of mutable static | = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior -error: borrow of packed field is unsafe and requires unsafe block (error E0133) - --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:24:5 - | -LL | &PACKED.data; - | ^^^^^^^^^^^^ borrow of packed field - | -note: the lint level is defined here - --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:4:9 - | -LL | #![deny(safe_packed_borrows)] - | ^^^^^^^^^^^^^^^^^^^ - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #46043 - = note: fields of packed structs might be misaligned: dereferencing a misaligned pointer or even just creating a misaligned reference is undefined behavior - error: call to unsafe function is unsafe and requires unsafe block (error E0133) - --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:33:5 + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:22:5 | LL | unsf(); | ^^^^^^ call to unsafe function | note: the lint level is defined here - --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:31:8 + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:20:8 | LL | #[deny(warnings)] | ^^^^^^^^ @@ -57,7 +42,7 @@ LL | #[deny(warnings)] = note: consult the function's documentation for information on how to avoid undefined behavior error: dereference of raw pointer is unsafe and requires unsafe block (error E0133) - --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:35:5 + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:24:5 | LL | *PTR; | ^^^^ dereference of raw pointer @@ -65,25 +50,15 @@ LL | *PTR; = note: raw pointers may be NULL, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior error: use of mutable static is unsafe and requires unsafe block (error E0133) - --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:37:5 + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:26:5 | LL | VOID = (); | ^^^^^^^^^ use of mutable static | = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior -error: borrow of packed field is unsafe and requires unsafe block (error E0133) - --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:39:5 - | -LL | &PACKED.data; - | ^^^^^^^^^^^^ borrow of packed field - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #46043 - = note: fields of packed structs might be misaligned: dereferencing a misaligned pointer or even just creating a misaligned reference is undefined behavior - error: unnecessary `unsafe` block - --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:55:14 + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:40:14 | LL | unsafe { unsafe { unsf() } } | ------ ^^^^^^ unnecessary `unsafe` block @@ -96,35 +71,20 @@ note: the lint level is defined here LL | #![deny(unused_unsafe)] | ^^^^^^^^^^^^^ -warning: borrow of packed field is unsafe and requires unsafe block (error E0133) - --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:61:5 - | -LL | &PACKED.data; - | ^^^^^^^^^^^^ borrow of packed field - | -note: the lint level is defined here - --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:59:8 - | -LL | #[warn(safe_packed_borrows)] - | ^^^^^^^^^^^^^^^^^^^ - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #46043 - = note: fields of packed structs might be misaligned: dereferencing a misaligned pointer or even just creating a misaligned reference is undefined behavior - error: unnecessary `unsafe` block - --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:79:5 + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:51:5 | LL | unsafe { unsf() } | ^^^^^^ unnecessary `unsafe` block error: unnecessary `unsafe` block - --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:92:9 + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:63:9 | LL | unsafe { unsf() } | ^^^^^^ unnecessary `unsafe` block error[E0133]: call to unsafe function is unsafe and requires unsafe block - --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:98:5 + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:69:5 | LL | unsf(); | ^^^^^^ call to unsafe function @@ -132,13 +92,13 @@ LL | unsf(); = note: consult the function's documentation for information on how to avoid undefined behavior error[E0133]: call to unsafe function is unsafe and requires unsafe function or block - --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:102:9 + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:73:9 | LL | unsf(); | ^^^^^^ call to unsafe function | = note: consult the function's documentation for information on how to avoid undefined behavior -error: aborting due to 13 previous errors; 1 warning emitted +error: aborting due to 11 previous errors For more information about this error, try `rustc --explain E0133`. From 3599ada976568d0b5326e890d4b0c2f22dcdc985 Mon Sep 17 00:00:00 2001 From: LeSeulArtichaut Date: Sat, 23 May 2020 22:15:14 +0200 Subject: [PATCH 36/56] Mark deduplicated errors as expected in gate test --- .../ui/feature-gates/feature-gate-unsafe_block_in_unsafe_fn.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/ui/feature-gates/feature-gate-unsafe_block_in_unsafe_fn.rs b/src/test/ui/feature-gates/feature-gate-unsafe_block_in_unsafe_fn.rs index bf33ac67fcae..61e512a12a18 100644 --- a/src/test/ui/feature-gates/feature-gate-unsafe_block_in_unsafe_fn.rs +++ b/src/test/ui/feature-gates/feature-gate-unsafe_block_in_unsafe_fn.rs @@ -1,4 +1,6 @@ #![deny(unsafe_op_in_unsafe_fn)] //~^ ERROR the `unsafe_op_in_unsafe_fn` lint is unstable +//~| ERROR the `unsafe_op_in_unsafe_fn` lint is unstable +//~| ERROR the `unsafe_op_in_unsafe_fn` lint is unstable fn main() {} From 4a538d31e056c7563d7e29a6bc7c89e9e46d8ee8 Mon Sep 17 00:00:00 2001 From: LeSeulArtichaut Date: Sat, 23 May 2020 22:22:01 +0200 Subject: [PATCH 37/56] Do not hardcode lint name --- src/librustc_lint/levels.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/librustc_lint/levels.rs b/src/librustc_lint/levels.rs index 706947201540..2e3fc162b530 100644 --- a/src/librustc_lint/levels.rs +++ b/src/librustc_lint/levels.rs @@ -9,6 +9,7 @@ use rustc_errors::{struct_span_err, Applicability}; use rustc_hir as hir; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_hir::{intravisit, HirId}; +use rustc_lint::builtin::UNSAFE_OP_IN_UNSAFE_FN; use rustc_middle::hir::map::Map; use rustc_middle::lint::LintDiagnosticBuilder; use rustc_middle::lint::{struct_lint_level, LintLevelMap, LintLevelSets, LintSet, LintSource}; @@ -387,7 +388,7 @@ impl<'s> LintLevelsBuilder<'s> { } fn check_gated_lint(&self, name: Symbol, span: Span) { - if name.as_str() == "unsafe_op_in_unsafe_fn" + if name.as_str() == UNSAFE_OP_IN_UNSAFE_FN.name && !self.sess.features_untracked().unsafe_block_in_unsafe_fn { feature_err( From e3d27ec1c82e223277b16f30e2355056144ca0da Mon Sep 17 00:00:00 2001 From: LeSeulArtichaut Date: Sat, 23 May 2020 22:32:40 +0200 Subject: [PATCH 38/56] Add explanation about taking the minimum of the two lints --- src/librustc_mir/transform/check_unsafety.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/librustc_mir/transform/check_unsafety.rs b/src/librustc_mir/transform/check_unsafety.rs index 7be640ae2431..1f01bc0e1951 100644 --- a/src/librustc_mir/transform/check_unsafety.rs +++ b/src/librustc_mir/transform/check_unsafety.rs @@ -726,6 +726,17 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: DefId) { }, ), UnsafetyViolationKind::UnsafeFnBorrowPacked => { + // When `unsafe_op_in_unsafe_fn` is disallowed, the behavior of safe and unsafe functions + // should be the same in terms of warnings and errors. Therefore, with `#[warn(safe_packed_borrows)]`, + // a safe packed borrow should emit a warning *but not an error* in an unsafe function, + // just like in a safe function, even if `unsafe_op_in_unsafe_fn` is `deny`. + // + // Also, `#[warn(unsafe_op_in_unsafe_fn)]` can't cause any new errors. Therefore, with + // `#[deny(safe_packed_borrows)]` and `#[warn(unsafe_op_in_unsafe_fn)]`, a packed borrow + // should only issue a warning for the sake of backwards compatibility. + // + // The solution those 2 expectations is to always take the minimum of both lints. + // This prevent any new errors (unless both lints are explicitely set to `deny`). let lint = if tcx.lint_level_at_node(SAFE_PACKED_BORROWS, lint_root).0 <= tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, lint_root).0 { From 1b0885062266a21e67fdeb12320c92ac7c00742e Mon Sep 17 00:00:00 2001 From: LeSeulArtichaut Date: Sat, 23 May 2020 22:39:07 +0200 Subject: [PATCH 39/56] Fix import --- src/librustc_lint/levels.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/librustc_lint/levels.rs b/src/librustc_lint/levels.rs index 2e3fc162b530..8dd683ae7995 100644 --- a/src/librustc_lint/levels.rs +++ b/src/librustc_lint/levels.rs @@ -9,7 +9,6 @@ use rustc_errors::{struct_span_err, Applicability}; use rustc_hir as hir; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_hir::{intravisit, HirId}; -use rustc_lint::builtin::UNSAFE_OP_IN_UNSAFE_FN; use rustc_middle::hir::map::Map; use rustc_middle::lint::LintDiagnosticBuilder; use rustc_middle::lint::{struct_lint_level, LintLevelMap, LintLevelSets, LintSet, LintSource}; @@ -388,7 +387,7 @@ impl<'s> LintLevelsBuilder<'s> { } fn check_gated_lint(&self, name: Symbol, span: Span) { - if name.as_str() == UNSAFE_OP_IN_UNSAFE_FN.name + if name.as_str() == builtin::UNSAFE_OP_IN_UNSAFE_FN.name && !self.sess.features_untracked().unsafe_block_in_unsafe_fn { feature_err( From 63066c0c06de27bc84f7fbbe683b59d4cb0441db Mon Sep 17 00:00:00 2001 From: LeSeulArtichaut Date: Sat, 23 May 2020 23:11:55 +0200 Subject: [PATCH 40/56] Use `LintId`s to check for gated lints --- src/librustc_lint/levels.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/librustc_lint/levels.rs b/src/librustc_lint/levels.rs index 8dd683ae7995..3d2ddf12a0a1 100644 --- a/src/librustc_lint/levels.rs +++ b/src/librustc_lint/levels.rs @@ -14,7 +14,7 @@ use rustc_middle::lint::LintDiagnosticBuilder; use rustc_middle::lint::{struct_lint_level, LintLevelMap, LintLevelSets, LintSet, LintSource}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::TyCtxt; -use rustc_session::lint::{builtin, Level, Lint}; +use rustc_session::lint::{builtin, Level, Lint, LintId}; use rustc_session::parse::feature_err; use rustc_session::Session; use rustc_span::symbol::{sym, Symbol}; @@ -80,13 +80,13 @@ impl<'s> LintLevelsBuilder<'s> { let level = cmp::min(level, self.sets.lint_cap); let lint_flag_val = Symbol::intern(lint_name); - self.check_gated_lint(lint_flag_val, DUMMY_SP); let ids = match store.find_lints(&lint_name) { Ok(ids) => ids, Err(_) => continue, // errors handled in check_lint_name_cmdline above }; for id in ids { + self.check_gated_lint(id, DUMMY_SP); let src = LintSource::CommandLine(lint_flag_val); specs.insert(id, (level, src)); } @@ -213,9 +213,9 @@ impl<'s> LintLevelsBuilder<'s> { let name = meta_item.path.segments.last().expect("empty lint name").ident.name; match store.check_lint_name(&name.as_str(), tool_name) { CheckLintNameResult::Ok(ids) => { - self.check_gated_lint(name, attr.span); let src = LintSource::Node(name, li.span(), reason); for id in ids { + self.check_gated_lint(*id, attr.span); specs.insert(*id, (level, src)); } } @@ -386,8 +386,8 @@ impl<'s> LintLevelsBuilder<'s> { BuilderPush { prev, changed: prev != self.cur } } - fn check_gated_lint(&self, name: Symbol, span: Span) { - if name.as_str() == builtin::UNSAFE_OP_IN_UNSAFE_FN.name + fn check_gated_lint(&self, id: LintId, span: Span) { + if id == LintId::of(builtin::UNSAFE_OP_IN_UNSAFE_FN) && !self.sess.features_untracked().unsafe_block_in_unsafe_fn { feature_err( From db684beb4e40aae70a38e22e4aa91251349bf49b Mon Sep 17 00:00:00 2001 From: LeSeulArtichaut Date: Wed, 27 May 2020 19:32:00 +0200 Subject: [PATCH 41/56] Whitelist `unsafe_op_in_unsafe_fn` in rustdoc --- src/librustdoc/core.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index bf59b3f25734..331ce1453e13 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -226,6 +226,11 @@ where { let warnings_lint_name = lint::builtin::WARNINGS.name; + // Whitelist feature-gated lints to avoid feature errors when trying to + // allow all lints. + // FIXME(LeSeulArtichaut): handle feature-gated lints properly. + let unsafe_op_in_unsafe_fn_name = rustc_lint::builtin::UNSAFE_OP_IN_UNSAFE_FN.name; + whitelisted_lints.push(warnings_lint_name.to_owned()); whitelisted_lints.extend(lint_opts.iter().map(|(lint, _)| lint).cloned()); @@ -236,7 +241,13 @@ where }; let lint_opts = lints() - .filter_map(|lint| if lint.name == warnings_lint_name { None } else { filter_call(lint) }) + .filter_map(|lint| { + if lint.name == warnings_lint_name || lint.name == unsafe_op_in_unsafe_fn_name { + None + } else { + filter_call(lint) + } + }) .chain(lint_opts.into_iter()) .collect::>(); From 3fea832fd7cbff73cf8d5d409ea9aeee0577b0ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 19 Dec 2019 19:44:06 -0800 Subject: [PATCH 42/56] Fix spacing of expected/found notes without a label --- src/librustc_errors/diagnostic.rs | 15 ++++++++++++--- .../project-fn-ret-invariant.transmute.stderr | 8 ++++---- .../dyn-trait.stderr | 8 ++++---- src/test/ui/issues/issue-16683.stderr | 4 ++-- src/test/ui/issues/issue-17758.stderr | 4 ++-- src/test/ui/issues/issue-20831-debruijn.stderr | 4 ++-- src/test/ui/issues/issue-52213.stderr | 4 ++-- src/test/ui/issues/issue-55796.stderr | 8 ++++---- src/test/ui/nll/issue-55394.stderr | 4 ++-- .../ui/nll/normalization-bounds-error.stderr | 4 ++-- src/test/ui/nll/type-alias-free-regions.stderr | 16 ++++++++-------- .../constant-in-expr-inherent-1.stderr | 4 ++-- .../constant-in-expr-trait-item-3.stderr | 4 ++-- .../object-lifetime-default-elision.stderr | 8 ++++---- .../region-object-lifetime-in-coercion.stderr | 8 ++++---- ...soc-type-region-bound-in-trait-not-met.stderr | 8 ++++---- ...soc-type-static-bound-in-trait-not-met.stderr | 4 ++-- .../regions-close-object-into-object-2.stderr | 4 ++-- .../regions-close-object-into-object-4.stderr | 4 ++-- ...ons-close-over-type-parameter-multiple.stderr | 4 ++-- .../ui/regions/regions-creating-enums4.stderr | 8 ++++---- src/test/ui/regions/regions-nested-fns.stderr | 4 ++-- ...regions-normalize-in-where-clause-list.stderr | 4 ++-- .../ui/regions/regions-ret-borrowed-1.stderr | 4 ++-- src/test/ui/regions/regions-ret-borrowed.stderr | 4 ++-- .../regions-trait-object-subtyping.stderr | 4 ++-- src/test/ui/reject-specialized-drops-8142.stderr | 4 ++-- ...pertrait-has-wrong-lifetime-parameters.stderr | 4 ++-- .../dyn-trait-underscore.stderr | 4 ++-- 29 files changed, 88 insertions(+), 79 deletions(-) diff --git a/src/librustc_errors/diagnostic.rs b/src/librustc_errors/diagnostic.rs index 1cc5daafed14..cff83c3d5cda 100644 --- a/src/librustc_errors/diagnostic.rs +++ b/src/librustc_errors/diagnostic.rs @@ -193,9 +193,18 @@ impl Diagnostic { expected_extra: &dyn fmt::Display, found_extra: &dyn fmt::Display, ) -> &mut Self { - let expected_label = format!("expected {}", expected_label); - - let found_label = format!("found {}", found_label); + let expected_label = expected_label.to_string(); + let expected_label = if expected_label.is_empty() { + "expected".to_string() + } else { + format!("expected {}", expected_label) + }; + let found_label = found_label.to_string(); + let found_label = if found_label.is_empty() { + "found".to_string() + } else { + format!("found {}", found_label) + }; let (found_padding, expected_padding) = if expected_label.len() > found_label.len() { (expected_label.len() - found_label.len(), 0) } else { diff --git a/src/test/ui/associated-types/cache/project-fn-ret-invariant.transmute.stderr b/src/test/ui/associated-types/cache/project-fn-ret-invariant.transmute.stderr index 3e39c8a79244..137cb83ccd32 100644 --- a/src/test/ui/associated-types/cache/project-fn-ret-invariant.transmute.stderr +++ b/src/test/ui/associated-types/cache/project-fn-ret-invariant.transmute.stderr @@ -14,16 +14,16 @@ note: ...so that the expression is assignable | LL | bar(foo, x) | ^ - = note: expected `Type<'_>` - found `Type<'a>` + = note: expected `Type<'_>` + found `Type<'a>` = note: but, the lifetime must be valid for the static lifetime... note: ...so that the expression is assignable --> $DIR/project-fn-ret-invariant.rs:48:4 | LL | bar(foo, x) | ^^^^^^^^^^^ - = note: expected `Type<'static>` - found `Type<'_>` + = note: expected `Type<'static>` + found `Type<'_>` error: aborting due to previous error diff --git a/src/test/ui/impl-header-lifetime-elision/dyn-trait.stderr b/src/test/ui/impl-header-lifetime-elision/dyn-trait.stderr index 3300293bb36c..268008c21112 100644 --- a/src/test/ui/impl-header-lifetime-elision/dyn-trait.stderr +++ b/src/test/ui/impl-header-lifetime-elision/dyn-trait.stderr @@ -14,16 +14,16 @@ note: ...so that the expression is assignable | LL | static_val(x); | ^ - = note: expected `std::boxed::Box` - found `std::boxed::Box<(dyn std::fmt::Debug + 'a)>` + = note: expected `std::boxed::Box` + found `std::boxed::Box<(dyn std::fmt::Debug + 'a)>` = note: but, the lifetime must be valid for the static lifetime... note: ...so that the types are compatible --> $DIR/dyn-trait.rs:20:5 | LL | static_val(x); | ^^^^^^^^^^ - = note: expected `StaticTrait` - found `StaticTrait` + = note: expected `StaticTrait` + found `StaticTrait` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-16683.stderr b/src/test/ui/issues/issue-16683.stderr index 99700f2084e4..4f6583307581 100644 --- a/src/test/ui/issues/issue-16683.stderr +++ b/src/test/ui/issues/issue-16683.stderr @@ -26,8 +26,8 @@ note: ...so that the types are compatible | LL | self.a(); | ^ - = note: expected `&'a Self` - found `&Self` + = note: expected `&'a Self` + found `&Self` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-17758.stderr b/src/test/ui/issues/issue-17758.stderr index adfc3f508582..31788cfa61c4 100644 --- a/src/test/ui/issues/issue-17758.stderr +++ b/src/test/ui/issues/issue-17758.stderr @@ -27,8 +27,8 @@ note: ...so that the types are compatible | LL | self.foo(); | ^^^ - = note: expected `&'a Self` - found `&Self` + = note: expected `&'a Self` + found `&Self` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-20831-debruijn.stderr b/src/test/ui/issues/issue-20831-debruijn.stderr index a785a956ca9f..160a4213a404 100644 --- a/src/test/ui/issues/issue-20831-debruijn.stderr +++ b/src/test/ui/issues/issue-20831-debruijn.stderr @@ -117,8 +117,8 @@ note: ...so that the types are compatible | LL | fn subscribe(&mut self, t : Box::Output> + 'a>) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: expected `Publisher<'_>` - found `Publisher<'_>` + = note: expected `Publisher<'_>` + found `Publisher<'_>` error: aborting due to 4 previous errors diff --git a/src/test/ui/issues/issue-52213.stderr b/src/test/ui/issues/issue-52213.stderr index a8960f775636..7463af9332a7 100644 --- a/src/test/ui/issues/issue-52213.stderr +++ b/src/test/ui/issues/issue-52213.stderr @@ -14,8 +14,8 @@ note: ...so that the types are compatible | LL | match (&t,) { | ^^^^^ - = note: expected `(&&(T,),)` - found `(&&'a (T,),)` + = note: expected `(&&(T,),)` + found `(&&'a (T,),)` note: but, the lifetime must be valid for the lifetime `'b` as defined on the function body at 1:27... --> $DIR/issue-52213.rs:1:27 | diff --git a/src/test/ui/issues/issue-55796.stderr b/src/test/ui/issues/issue-55796.stderr index b8cafdc5c14b..6bfb7af54446 100644 --- a/src/test/ui/issues/issue-55796.stderr +++ b/src/test/ui/issues/issue-55796.stderr @@ -20,8 +20,8 @@ note: ...so that the expression is assignable | LL | Box::new(self.out_edges(u).map(|e| e.target())) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: expected `std::boxed::Box<(dyn std::iter::Iterator>::Node> + 'static)>` - found `std::boxed::Box>::Node>>` + = note: expected `std::boxed::Box<(dyn std::iter::Iterator>::Node> + 'static)>` + found `std::boxed::Box>::Node>>` error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements --> $DIR/issue-55796.rs:21:9 @@ -45,8 +45,8 @@ note: ...so that the expression is assignable | LL | Box::new(self.in_edges(u).map(|e| e.target())) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: expected `std::boxed::Box<(dyn std::iter::Iterator>::Node> + 'static)>` - found `std::boxed::Box>::Node>>` + = note: expected `std::boxed::Box<(dyn std::iter::Iterator>::Node> + 'static)>` + found `std::boxed::Box>::Node>>` error: aborting due to 2 previous errors diff --git a/src/test/ui/nll/issue-55394.stderr b/src/test/ui/nll/issue-55394.stderr index 69a6ab004fd9..ba8d91b8455b 100644 --- a/src/test/ui/nll/issue-55394.stderr +++ b/src/test/ui/nll/issue-55394.stderr @@ -26,8 +26,8 @@ note: ...so that the expression is assignable | LL | Foo { bar } | ^^^^^^^^^^^ - = note: expected `Foo<'_>` - found `Foo<'_>` + = note: expected `Foo<'_>` + found `Foo<'_>` error: aborting due to previous error diff --git a/src/test/ui/nll/normalization-bounds-error.stderr b/src/test/ui/nll/normalization-bounds-error.stderr index 58f206742f4f..d003acd879a7 100644 --- a/src/test/ui/nll/normalization-bounds-error.stderr +++ b/src/test/ui/nll/normalization-bounds-error.stderr @@ -19,8 +19,8 @@ note: ...so that the types are compatible | LL | fn visit_seq<'d, 'a: 'd>() -> <&'a () as Visitor<'d>>::Value {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: expected `Visitor<'d>` - found `Visitor<'_>` + = note: expected `Visitor<'d>` + found `Visitor<'_>` error: aborting due to previous error diff --git a/src/test/ui/nll/type-alias-free-regions.stderr b/src/test/ui/nll/type-alias-free-regions.stderr index 5191deca281c..3317aae83bb0 100644 --- a/src/test/ui/nll/type-alias-free-regions.stderr +++ b/src/test/ui/nll/type-alias-free-regions.stderr @@ -16,8 +16,8 @@ note: ...so that the expression is assignable | LL | C { f: b } | ^ - = note: expected `std::boxed::Box>` - found `std::boxed::Box>` + = note: expected `std::boxed::Box>` + found `std::boxed::Box>` note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 15:6... --> $DIR/type-alias-free-regions.rs:15:6 | @@ -28,8 +28,8 @@ note: ...so that the expression is assignable | LL | C { f: b } | ^^^^^^^^^^ - = note: expected `C<'a>` - found `C<'_>` + = note: expected `C<'a>` + found `C<'_>` error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements --> $DIR/type-alias-free-regions.rs:27:16 @@ -49,8 +49,8 @@ note: ...so that the expression is assignable | LL | C { f: Box::new(b.0) } | ^^^ - = note: expected `std::boxed::Box<&isize>` - found `std::boxed::Box<&isize>` + = note: expected `std::boxed::Box<&isize>` + found `std::boxed::Box<&isize>` note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 25:6... --> $DIR/type-alias-free-regions.rs:25:6 | @@ -61,8 +61,8 @@ note: ...so that the expression is assignable | LL | C { f: Box::new(b.0) } | ^^^^^^^^^^^^^^^^^^^^^^ - = note: expected `C<'a>` - found `C<'_>` + = note: expected `C<'a>` + found `C<'_>` error: aborting due to 2 previous errors diff --git a/src/test/ui/nll/user-annotations/constant-in-expr-inherent-1.stderr b/src/test/ui/nll/user-annotations/constant-in-expr-inherent-1.stderr index 37be450fd0a7..8421dc1d0c13 100644 --- a/src/test/ui/nll/user-annotations/constant-in-expr-inherent-1.stderr +++ b/src/test/ui/nll/user-annotations/constant-in-expr-inherent-1.stderr @@ -14,8 +14,8 @@ note: ...so that the types are compatible | LL | >::C | ^^^^^^^^^^^^ - = note: expected `Foo<'_>` - found `Foo<'a>` + = note: expected `Foo<'_>` + found `Foo<'a>` = note: but, the lifetime must be valid for the static lifetime... note: ...so that reference does not outlive borrowed content --> $DIR/constant-in-expr-inherent-1.rs:8:5 diff --git a/src/test/ui/nll/user-annotations/constant-in-expr-trait-item-3.stderr b/src/test/ui/nll/user-annotations/constant-in-expr-trait-item-3.stderr index 4ee32847c5ec..ba0a1748c5e9 100644 --- a/src/test/ui/nll/user-annotations/constant-in-expr-trait-item-3.stderr +++ b/src/test/ui/nll/user-annotations/constant-in-expr-trait-item-3.stderr @@ -14,8 +14,8 @@ note: ...so that the types are compatible | LL | T::C | ^^^^ - = note: expected `Foo<'_>` - found `Foo<'a>` + = note: expected `Foo<'_>` + found `Foo<'a>` = note: but, the lifetime must be valid for the static lifetime... note: ...so that reference does not outlive borrowed content --> $DIR/constant-in-expr-trait-item-3.rs:10:5 diff --git a/src/test/ui/object-lifetime/object-lifetime-default-elision.stderr b/src/test/ui/object-lifetime/object-lifetime-default-elision.stderr index 1952ee8269d5..79ded5fc875a 100644 --- a/src/test/ui/object-lifetime/object-lifetime-default-elision.stderr +++ b/src/test/ui/object-lifetime/object-lifetime-default-elision.stderr @@ -24,8 +24,8 @@ note: ...so that the expression is assignable | LL | ss | ^^ - = note: expected `&'b (dyn SomeTrait + 'b)` - found `&dyn SomeTrait` + = note: expected `&'b (dyn SomeTrait + 'b)` + found `&dyn SomeTrait` error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements --> $DIR/object-lifetime-default-elision.rs:71:5 @@ -53,8 +53,8 @@ note: ...so that the expression is assignable | LL | ss | ^^ - = note: expected `&'b (dyn SomeTrait + 'b)` - found `&dyn SomeTrait` + = note: expected `&'b (dyn SomeTrait + 'b)` + found `&dyn SomeTrait` error: aborting due to 2 previous errors diff --git a/src/test/ui/regions/region-object-lifetime-in-coercion.stderr b/src/test/ui/regions/region-object-lifetime-in-coercion.stderr index e88965164703..069b897603cb 100644 --- a/src/test/ui/regions/region-object-lifetime-in-coercion.stderr +++ b/src/test/ui/regions/region-object-lifetime-in-coercion.stderr @@ -39,8 +39,8 @@ note: ...so that the expression is assignable | LL | Box::new(v) | ^ - = note: expected `&[u8]` - found `&'a [u8]` + = note: expected `&[u8]` + found `&'a [u8]` note: but, the lifetime must be valid for the lifetime `'b` as defined on the function body at 25:9... --> $DIR/region-object-lifetime-in-coercion.rs:25:9 | @@ -51,8 +51,8 @@ note: ...so that the expression is assignable | LL | Box::new(v) | ^^^^^^^^^^^ - = note: expected `std::boxed::Box<(dyn Foo + 'b)>` - found `std::boxed::Box` + = note: expected `std::boxed::Box<(dyn Foo + 'b)>` + found `std::boxed::Box` error: aborting due to 4 previous errors diff --git a/src/test/ui/regions/regions-assoc-type-region-bound-in-trait-not-met.stderr b/src/test/ui/regions/regions-assoc-type-region-bound-in-trait-not-met.stderr index 865e967fba32..c134b3b3ed55 100644 --- a/src/test/ui/regions/regions-assoc-type-region-bound-in-trait-not-met.stderr +++ b/src/test/ui/regions/regions-assoc-type-region-bound-in-trait-not-met.stderr @@ -14,8 +14,8 @@ note: ...so that the types are compatible | LL | impl<'a> Foo<'static> for &'a i32 { | ^^^^^^^^^^^^ - = note: expected `Foo<'static>` - found `Foo<'static>` + = note: expected `Foo<'static>` + found `Foo<'static>` = note: but, the lifetime must be valid for the static lifetime... note: ...so that the type `&i32` will meet its required lifetime bounds --> $DIR/regions-assoc-type-region-bound-in-trait-not-met.rs:14:10 @@ -39,8 +39,8 @@ note: ...so that the types are compatible | LL | impl<'a,'b> Foo<'b> for &'a i64 { | ^^^^^^^ - = note: expected `Foo<'b>` - found `Foo<'_>` + = note: expected `Foo<'b>` + found `Foo<'_>` note: but, the lifetime must be valid for the lifetime `'b` as defined on the impl at 19:9... --> $DIR/regions-assoc-type-region-bound-in-trait-not-met.rs:19:9 | diff --git a/src/test/ui/regions/regions-assoc-type-static-bound-in-trait-not-met.stderr b/src/test/ui/regions/regions-assoc-type-static-bound-in-trait-not-met.stderr index 6a34871c07ef..ac8c55ccc8fd 100644 --- a/src/test/ui/regions/regions-assoc-type-static-bound-in-trait-not-met.stderr +++ b/src/test/ui/regions/regions-assoc-type-static-bound-in-trait-not-met.stderr @@ -14,8 +14,8 @@ note: ...so that the types are compatible | LL | impl<'a> Foo for &'a i32 { | ^^^ - = note: expected `Foo` - found `Foo` + = note: expected `Foo` + found `Foo` = note: but, the lifetime must be valid for the static lifetime... note: ...so that the type `&i32` will meet its required lifetime bounds --> $DIR/regions-assoc-type-static-bound-in-trait-not-met.rs:9:10 diff --git a/src/test/ui/regions/regions-close-object-into-object-2.stderr b/src/test/ui/regions/regions-close-object-into-object-2.stderr index 28873ab807f8..147f7f354181 100644 --- a/src/test/ui/regions/regions-close-object-into-object-2.stderr +++ b/src/test/ui/regions/regions-close-object-into-object-2.stderr @@ -20,8 +20,8 @@ note: ...so that the expression is assignable | LL | box B(&*v) as Box | ^^^^^^^^^^^^^^^^^^^^^^^^ - = note: expected `std::boxed::Box<(dyn X + 'static)>` - found `std::boxed::Box` + = note: expected `std::boxed::Box<(dyn X + 'static)>` + found `std::boxed::Box` error: aborting due to previous error diff --git a/src/test/ui/regions/regions-close-object-into-object-4.stderr b/src/test/ui/regions/regions-close-object-into-object-4.stderr index 449a5b5fdd4d..6e7d6152cd09 100644 --- a/src/test/ui/regions/regions-close-object-into-object-4.stderr +++ b/src/test/ui/regions/regions-close-object-into-object-4.stderr @@ -20,8 +20,8 @@ note: ...so that the expression is assignable | LL | box B(&*v) as Box | ^^^^^^^^^^^^^^^^^^^^^^^^ - = note: expected `std::boxed::Box<(dyn X + 'static)>` - found `std::boxed::Box` + = note: expected `std::boxed::Box<(dyn X + 'static)>` + found `std::boxed::Box` error: aborting due to previous error diff --git a/src/test/ui/regions/regions-close-over-type-parameter-multiple.stderr b/src/test/ui/regions/regions-close-over-type-parameter-multiple.stderr index b2a7afaf1b45..2070ce257b18 100644 --- a/src/test/ui/regions/regions-close-over-type-parameter-multiple.stderr +++ b/src/test/ui/regions/regions-close-over-type-parameter-multiple.stderr @@ -24,8 +24,8 @@ note: ...so that the expression is assignable | LL | box v as Box | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: expected `std::boxed::Box<(dyn SomeTrait + 'c)>` - found `std::boxed::Box` + = note: expected `std::boxed::Box<(dyn SomeTrait + 'c)>` + found `std::boxed::Box` error: aborting due to previous error diff --git a/src/test/ui/regions/regions-creating-enums4.stderr b/src/test/ui/regions/regions-creating-enums4.stderr index 58f74e4ee142..b24db1df18b0 100644 --- a/src/test/ui/regions/regions-creating-enums4.stderr +++ b/src/test/ui/regions/regions-creating-enums4.stderr @@ -14,8 +14,8 @@ note: ...so that the expression is assignable | LL | Ast::Add(x, y) | ^ - = note: expected `&Ast<'_>` - found `&Ast<'a>` + = note: expected `&Ast<'_>` + found `&Ast<'a>` note: but, the lifetime must be valid for the lifetime `'b` as defined on the function body at 6:19... --> $DIR/regions-creating-enums4.rs:6:19 | @@ -26,8 +26,8 @@ note: ...so that the expression is assignable | LL | Ast::Add(x, y) | ^^^^^^^^^^^^^^ - = note: expected `Ast<'b>` - found `Ast<'_>` + = note: expected `Ast<'b>` + found `Ast<'_>` error: aborting due to previous error diff --git a/src/test/ui/regions/regions-nested-fns.stderr b/src/test/ui/regions/regions-nested-fns.stderr index 8fce1609d783..9e405d83140d 100644 --- a/src/test/ui/regions/regions-nested-fns.stderr +++ b/src/test/ui/regions/regions-nested-fns.stderr @@ -39,8 +39,8 @@ LL | | if false { return ay; } LL | | return z; LL | | })); | |_____^ - = note: expected `&isize` - found `&isize` + = note: expected `&isize` + found `&isize` error[E0312]: lifetime of reference outlives lifetime of borrowed content... --> $DIR/regions-nested-fns.rs:14:27 diff --git a/src/test/ui/regions/regions-normalize-in-where-clause-list.stderr b/src/test/ui/regions/regions-normalize-in-where-clause-list.stderr index c35516d2c087..a274fd9d658a 100644 --- a/src/test/ui/regions/regions-normalize-in-where-clause-list.stderr +++ b/src/test/ui/regions/regions-normalize-in-where-clause-list.stderr @@ -29,8 +29,8 @@ LL | | where <() as Project<'a, 'b>>::Item : Eq LL | | { LL | | } | |_^ - = note: expected `Project<'a, 'b>` - found `Project<'_, '_>` + = note: expected `Project<'a, 'b>` + found `Project<'_, '_>` error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements --> $DIR/regions-normalize-in-where-clause-list.rs:22:1 diff --git a/src/test/ui/regions/regions-ret-borrowed-1.stderr b/src/test/ui/regions/regions-ret-borrowed-1.stderr index 2895a0ccdeec..2c4769d8e375 100644 --- a/src/test/ui/regions/regions-ret-borrowed-1.stderr +++ b/src/test/ui/regions/regions-ret-borrowed-1.stderr @@ -14,8 +14,8 @@ note: ...so that the expression is assignable | LL | with(|o| o) | ^ - = note: expected `&isize` - found `&isize` + = note: expected `&isize` + found `&isize` note: but, the lifetime must be valid for the lifetime `'a` as defined on the function body at 9:14... --> $DIR/regions-ret-borrowed-1.rs:9:14 | diff --git a/src/test/ui/regions/regions-ret-borrowed.stderr b/src/test/ui/regions/regions-ret-borrowed.stderr index b74f10f5075e..da560107cea9 100644 --- a/src/test/ui/regions/regions-ret-borrowed.stderr +++ b/src/test/ui/regions/regions-ret-borrowed.stderr @@ -14,8 +14,8 @@ note: ...so that the expression is assignable | LL | with(|o| o) | ^ - = note: expected `&isize` - found `&isize` + = note: expected `&isize` + found `&isize` note: but, the lifetime must be valid for the lifetime `'a` as defined on the function body at 12:14... --> $DIR/regions-ret-borrowed.rs:12:14 | diff --git a/src/test/ui/regions/regions-trait-object-subtyping.stderr b/src/test/ui/regions/regions-trait-object-subtyping.stderr index 58b79d212700..7478b53bd3cc 100644 --- a/src/test/ui/regions/regions-trait-object-subtyping.stderr +++ b/src/test/ui/regions/regions-trait-object-subtyping.stderr @@ -41,8 +41,8 @@ note: ...so that the expression is assignable | LL | x | ^ - = note: expected `&'b mut (dyn Dummy + 'b)` - found `&mut (dyn Dummy + 'b)` + = note: expected `&'b mut (dyn Dummy + 'b)` + found `&mut (dyn Dummy + 'b)` error[E0308]: mismatched types --> $DIR/regions-trait-object-subtyping.rs:22:5 diff --git a/src/test/ui/reject-specialized-drops-8142.stderr b/src/test/ui/reject-specialized-drops-8142.stderr index c09418de5183..f819faa27899 100644 --- a/src/test/ui/reject-specialized-drops-8142.stderr +++ b/src/test/ui/reject-specialized-drops-8142.stderr @@ -118,8 +118,8 @@ note: ...so that the types are compatible | LL | impl<'lw> Drop for W<'lw,'lw> { fn drop(&mut self) { } } // REJECT | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: expected `W<'l1, 'l2>` - found `W<'_, '_>` + = note: expected `W<'l1, 'l2>` + found `W<'_, '_>` error[E0367]: `Drop` impl requires `AddsBnd: Bound` but the enum it is implemented for does not --> $DIR/reject-specialized-drops-8142.rs:61:14 diff --git a/src/test/ui/traits/trait-impl-of-supertrait-has-wrong-lifetime-parameters.stderr b/src/test/ui/traits/trait-impl-of-supertrait-has-wrong-lifetime-parameters.stderr index 9fdcd4de495c..46aa7db967ad 100644 --- a/src/test/ui/traits/trait-impl-of-supertrait-has-wrong-lifetime-parameters.stderr +++ b/src/test/ui/traits/trait-impl-of-supertrait-has-wrong-lifetime-parameters.stderr @@ -19,8 +19,8 @@ note: ...so that the types are compatible | LL | impl<'a,'b> T2<'a, 'b> for S<'a, 'b> { | ^^^^^^^^^^ - = note: expected `T1<'a>` - found `T1<'_>` + = note: expected `T1<'a>` + found `T1<'_>` error: aborting due to previous error diff --git a/src/test/ui/underscore-lifetime/dyn-trait-underscore.stderr b/src/test/ui/underscore-lifetime/dyn-trait-underscore.stderr index e6029e0d4623..e3c9d50dfe5b 100644 --- a/src/test/ui/underscore-lifetime/dyn-trait-underscore.stderr +++ b/src/test/ui/underscore-lifetime/dyn-trait-underscore.stderr @@ -23,8 +23,8 @@ note: ...so that the expression is assignable | LL | Box::new(items.iter()) | ^^^^^^^^^^^^^^^^^^^^^^ - = note: expected `std::boxed::Box<(dyn std::iter::Iterator + 'static)>` - found `std::boxed::Box>` + = note: expected `std::boxed::Box<(dyn std::iter::Iterator + 'static)>` + found `std::boxed::Box>` error: aborting due to previous error From 5ba22205a48f3c4232a899effc47fa1925ed9900 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 20 Dec 2019 11:56:03 -0800 Subject: [PATCH 43/56] Name `RegionKind::ReVar` lifetimes in diagnostics --- .../infer/error_reporting/mod.rs | 9 ++-- .../trait_impl_difference.rs | 51 ++++++++++++++++++- src/test/ui/coercion/coerce-mut.rs | 4 +- src/test/ui/coercion/coerce-mut.stderr | 4 +- .../reordered-type-param.stderr | 2 +- src/test/ui/hrtb/hrtb-exists-forall-fn.stderr | 2 +- .../impl-generic-mismatch-ab.stderr | 2 +- src/test/ui/impl-trait/trait_type.stderr | 2 +- .../mismatched_trait_impl-2.stderr | 4 +- .../mismatched_trait_impl.stderr | 4 +- src/test/ui/issues/issue-13033.rs | 2 +- src/test/ui/issues/issue-13033.stderr | 2 +- src/test/ui/issues/issue-16683.stderr | 2 +- src/test/ui/issues/issue-17758.stderr | 2 +- src/test/ui/issues/issue-20225.stderr | 8 ++- src/test/ui/issues/issue-21332.stderr | 2 +- ...ime-mismatch-between-trait-and-impl.stderr | 4 +- src/test/ui/mismatched_types/E0053.stderr | 2 +- .../trait-impl-fn-incompatibility.stderr | 2 +- .../ui/nll/type-alias-free-regions.stderr | 4 +- ...ons-fn-subtyping-return-static-fail.stderr | 2 +- .../region-object-lifetime-in-coercion.stderr | 2 +- .../regions-fn-subtyping-return-static.stderr | 2 +- src/test/ui/regions/regions-nested-fns.stderr | 2 +- .../ui/regions/regions-ret-borrowed-1.stderr | 2 +- .../ui/regions/regions-ret-borrowed.stderr | 2 +- src/test/ui/regions/regions-trait-1.stderr | 2 +- .../regions-trait-object-subtyping.stderr | 2 +- .../resolve-inconsistent-binding-mode.stderr | 4 +- src/test/ui/span/coerce-suggestions.stderr | 4 +- .../traits/trait-impl-method-mismatch.stderr | 2 +- src/test/ui/type/type-mismatch.stderr | 4 +- src/test/ui/unsafe/unsafe-trait-impl.rs | 2 +- src/test/ui/unsafe/unsafe-trait-impl.stderr | 2 +- src/test/ui/wrong-mul-method-signature.stderr | 2 +- 35 files changed, 105 insertions(+), 45 deletions(-) diff --git a/src/librustc_infer/infer/error_reporting/mod.rs b/src/librustc_infer/infer/error_reporting/mod.rs index cc479aa17ce9..66e7c87f2c35 100644 --- a/src/librustc_infer/infer/error_reporting/mod.rs +++ b/src/librustc_infer/infer/error_reporting/mod.rs @@ -987,13 +987,16 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } fn push_ty_ref<'tcx>( - r: &ty::Region<'tcx>, + region: &ty::Region<'tcx>, ty: Ty<'tcx>, mutbl: hir::Mutability, s: &mut DiagnosticStyledString, ) { - let mut r = r.to_string(); - if r == "'_" { + let mut r = region.to_string(); + if let ty::RegionKind::ReVar(var) = region { + // Show these named, not as `'_` or elide them in "expected/found" notes. + r = format!("'z{} ", var.index()); + } else if r == "'_" { r.clear(); } else { r.push(' '); diff --git a/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs b/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs index 695f3e47fb5d..2e54a4e28e98 100644 --- a/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs +++ b/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs @@ -1,11 +1,15 @@ //! Error Reporting for `impl` items that do not match the obligations from their `trait`. +use crate::hir::def_id::DefId; use crate::infer::error_reporting::nice_region_error::NiceRegionError; use crate::infer::lexical_region_resolve::RegionResolutionError; use crate::infer::{Subtype, ValuePairs}; use crate::traits::ObligationCauseCode::CompareImplMethodObligation; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::ErrorReported; -use rustc_middle::ty::Ty; +use rustc_middle::ty::error::ExpectedFound; +use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::ty::{self, Ty}; use rustc_span::Span; impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { @@ -52,9 +56,52 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { .tcx() .sess .struct_span_err(sp, "`impl` item signature doesn't match `trait` item signature"); - err.note(&format!("expected `{:?}`\n found `{:?}`", expected, found)); err.span_label(sp, &format!("found {:?}", found)); err.span_label(impl_sp, &format!("expected {:?}", expected)); + + struct EarlyBoundRegionHighlighter(FxHashSet); + impl<'tcx> ty::fold::TypeVisitor<'tcx> for EarlyBoundRegionHighlighter { + fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool { + debug!("LateBoundRegionNameCollector visit_region {:?}", r); + match *r { + ty::ReFree(free) => { + self.0.insert(free.scope); + } + + ty::ReEarlyBound(bound) => { + self.0.insert(bound.def_id); + } + _ => {} + } + r.super_visit_with(self) + } + } + + let mut visitor = EarlyBoundRegionHighlighter(FxHashSet::default()); + expected.visit_with(&mut visitor); + + let note = !visitor.0.is_empty(); + + if let Some((expected, found)) = self + .tcx() + .infer_ctxt() + .enter(|infcx| infcx.expected_found_str_ty(&ExpectedFound { expected, found })) + { + err.note_expected_found(&"", expected, &"", found); + } else { + // This fallback shouldn't be necessary, but let's keep it in just in case. + err.note(&format!("expected `{:?}`\n found `{:?}`", expected, found)); + } + if note { + err.note( + "the lifetime requirements from the `trait` could not be fulfilled by the \ + `impl`", + ); + err.help( + "consider adding a named lifetime to the `trait` that constrains the item's \ + `self` argument, its inputs and its output with it", + ); + } err.emit(); } } diff --git a/src/test/ui/coercion/coerce-mut.rs b/src/test/ui/coercion/coerce-mut.rs index 43f0b55856d3..3ccfe1cede7a 100644 --- a/src/test/ui/coercion/coerce-mut.rs +++ b/src/test/ui/coercion/coerce-mut.rs @@ -4,7 +4,7 @@ fn main() { let x = 0; f(&x); //~^ ERROR mismatched types - //~| expected mutable reference `&mut i32` - //~| found reference `&{integer}` + //~| expected mutable reference `&'z1 mut i32` + //~| found reference `&'z2 {integer}` //~| types differ in mutability } diff --git a/src/test/ui/coercion/coerce-mut.stderr b/src/test/ui/coercion/coerce-mut.stderr index 2601ca5e91e5..f5b13f6c5909 100644 --- a/src/test/ui/coercion/coerce-mut.stderr +++ b/src/test/ui/coercion/coerce-mut.stderr @@ -4,8 +4,8 @@ error[E0308]: mismatched types LL | f(&x); | ^^ types differ in mutability | - = note: expected mutable reference `&mut i32` - found reference `&{integer}` + = note: expected mutable reference `&'z1 mut i32` + found reference `&'z2 {integer}` error: aborting due to previous error diff --git a/src/test/ui/compare-method/reordered-type-param.stderr b/src/test/ui/compare-method/reordered-type-param.stderr index f1f8a663f212..9eed16fa994e 100644 --- a/src/test/ui/compare-method/reordered-type-param.stderr +++ b/src/test/ui/compare-method/reordered-type-param.stderr @@ -11,7 +11,7 @@ LL | fn b(&self, _x: G) -> G { panic!() } | expected type parameter | = note: expected fn pointer `fn(&E, F) -> F` - found fn pointer `fn(&E, G) -> G` + found fn pointer `fn(&'z0 E, G) -> G` = note: a type parameter was expected, but a different one was found; you might be missing a type parameter or trait bound = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters diff --git a/src/test/ui/hrtb/hrtb-exists-forall-fn.stderr b/src/test/ui/hrtb/hrtb-exists-forall-fn.stderr index 328e98657eff..4d09fe74e16f 100644 --- a/src/test/ui/hrtb/hrtb-exists-forall-fn.stderr +++ b/src/test/ui/hrtb/hrtb-exists-forall-fn.stderr @@ -7,7 +7,7 @@ LL | let _: for<'b> fn(&'b u32) = foo(); | expected due to this | = note: expected fn pointer `for<'b> fn(&'b u32)` - found fn pointer `fn(&u32)` + found fn pointer `fn(&'z0 u32)` error: aborting due to previous error diff --git a/src/test/ui/impl-trait/impl-generic-mismatch-ab.stderr b/src/test/ui/impl-trait/impl-generic-mismatch-ab.stderr index 638a0093fb21..d5725efbac3c 100644 --- a/src/test/ui/impl-trait/impl-generic-mismatch-ab.stderr +++ b/src/test/ui/impl-trait/impl-generic-mismatch-ab.stderr @@ -10,7 +10,7 @@ LL | fn foo(&self, a: &impl Debug, b: &B) { } | expected type parameter | = note: expected fn pointer `fn(&(), &B, &impl Debug)` - found fn pointer `fn(&(), &impl Debug, &B)` + found fn pointer `fn(&'z0 (), &impl Debug, &B)` = note: a type parameter was expected, but a different one was found; you might be missing a type parameter or trait bound = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters diff --git a/src/test/ui/impl-trait/trait_type.stderr b/src/test/ui/impl-trait/trait_type.stderr index 748bc639a03c..3cc9c1a7fb05 100644 --- a/src/test/ui/impl-trait/trait_type.stderr +++ b/src/test/ui/impl-trait/trait_type.stderr @@ -5,7 +5,7 @@ LL | fn fmt(&self, x: &str) -> () { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ in mutability | = note: expected fn pointer `fn(&MyType, &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error>` - found fn pointer `fn(&MyType, &str)` + found fn pointer `fn(&'z0 MyType, &str)` error[E0050]: method `fmt` has 1 parameter but the declaration in trait `std::fmt::Display::fmt` has 2 --> $DIR/trait_type.rs:12:11 diff --git a/src/test/ui/in-band-lifetimes/mismatched_trait_impl-2.stderr b/src/test/ui/in-band-lifetimes/mismatched_trait_impl-2.stderr index c1c4ec9ed7b9..349f813b20d5 100644 --- a/src/test/ui/in-band-lifetimes/mismatched_trait_impl-2.stderr +++ b/src/test/ui/in-band-lifetimes/mismatched_trait_impl-2.stderr @@ -10,7 +10,9 @@ LL | fn deref(&self) -> &Self::Target; | --------------------------------- expected fn(&Struct) -> &(dyn Trait + 'static) | = note: expected `fn(&Struct) -> &(dyn Trait + 'static)` - found `fn(&Struct) -> &dyn Trait` + found `fn(&'z0 Struct) -> &dyn Trait` + = note: the lifetime requirements from the `trait` could not be fulfilled by the `impl` + = help: consider adding a named lifetime to the `trait` that constrains the item's `self` argument, its inputs and its output with it error: aborting due to previous error diff --git a/src/test/ui/in-band-lifetimes/mismatched_trait_impl.stderr b/src/test/ui/in-band-lifetimes/mismatched_trait_impl.stderr index 15891c9e7a62..72960a41f93d 100644 --- a/src/test/ui/in-band-lifetimes/mismatched_trait_impl.stderr +++ b/src/test/ui/in-band-lifetimes/mismatched_trait_impl.stderr @@ -8,7 +8,9 @@ LL | fn foo(&self, x: &u32, y: &'a u32) -> &'a u32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found fn(&i32, &u32, &u32) -> &u32 | = note: expected `fn(&i32, &'a u32, &u32) -> &'a u32` - found `fn(&i32, &u32, &u32) -> &u32` + found `fn(&'z0 i32, &'z1 u32, &'z2 u32) -> &'z2 u32` + = note: the lifetime requirements from the `trait` could not be fulfilled by the `impl` + = help: consider adding a named lifetime to the `trait` that constrains the item's `self` argument, its inputs and its output with it error[E0623]: lifetime mismatch --> $DIR/mismatched_trait_impl.rs:10:9 diff --git a/src/test/ui/issues/issue-13033.rs b/src/test/ui/issues/issue-13033.rs index 7631831a81a5..3fa94fac2375 100644 --- a/src/test/ui/issues/issue-13033.rs +++ b/src/test/ui/issues/issue-13033.rs @@ -8,7 +8,7 @@ impl Foo for Baz { fn bar(&mut self, other: &dyn Foo) {} //~^ ERROR method `bar` has an incompatible type for trait //~| expected fn pointer `fn(&mut Baz, &mut dyn Foo)` - //~| found fn pointer `fn(&mut Baz, &dyn Foo)` + //~| found fn pointer `fn(&'z0 mut Baz, &dyn Foo)` } fn main() {} diff --git a/src/test/ui/issues/issue-13033.stderr b/src/test/ui/issues/issue-13033.stderr index a8473c8a5241..238f41782ba5 100644 --- a/src/test/ui/issues/issue-13033.stderr +++ b/src/test/ui/issues/issue-13033.stderr @@ -8,7 +8,7 @@ LL | fn bar(&mut self, other: &dyn Foo) {} | ^^^^^^^^ types differ in mutability | = note: expected fn pointer `fn(&mut Baz, &mut dyn Foo)` - found fn pointer `fn(&mut Baz, &dyn Foo)` + found fn pointer `fn(&'z0 mut Baz, &dyn Foo)` help: consider change the type to match the mutability in trait | LL | fn bar(&mut self, other: &mut dyn Foo) {} diff --git a/src/test/ui/issues/issue-16683.stderr b/src/test/ui/issues/issue-16683.stderr index 4f6583307581..fa782d043312 100644 --- a/src/test/ui/issues/issue-16683.stderr +++ b/src/test/ui/issues/issue-16683.stderr @@ -27,7 +27,7 @@ note: ...so that the types are compatible LL | self.a(); | ^ = note: expected `&'a Self` - found `&Self` + found `&'z0 Self` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-17758.stderr b/src/test/ui/issues/issue-17758.stderr index 31788cfa61c4..7c382b95ae9a 100644 --- a/src/test/ui/issues/issue-17758.stderr +++ b/src/test/ui/issues/issue-17758.stderr @@ -28,7 +28,7 @@ note: ...so that the types are compatible LL | self.foo(); | ^^^ = note: expected `&'a Self` - found `&Self` + found `&'z0 Self` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-20225.stderr b/src/test/ui/issues/issue-20225.stderr index 3bcc50ded842..351973d444b0 100644 --- a/src/test/ui/issues/issue-20225.stderr +++ b/src/test/ui/issues/issue-20225.stderr @@ -7,7 +7,9 @@ LL | extern "rust-call" fn call(&self, (_,): (T,)) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&T`, found type parameter `T` | = note: expected fn pointer `extern "rust-call" fn(&Foo, (&'a T,))` - found fn pointer `extern "rust-call" fn(&Foo, (T,))` + found fn pointer `extern "rust-call" fn(&'z0 Foo, (T,))` + = help: type parameters must be constrained to match other types + = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters error[E0053]: method `call_mut` has an incompatible type for trait --> $DIR/issue-20225.rs:11:3 @@ -18,7 +20,9 @@ LL | extern "rust-call" fn call_mut(&mut self, (_,): (T,)) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&T`, found type parameter `T` | = note: expected fn pointer `extern "rust-call" fn(&mut Foo, (&'a T,))` - found fn pointer `extern "rust-call" fn(&mut Foo, (T,))` + found fn pointer `extern "rust-call" fn(&'z0 mut Foo, (T,))` + = help: type parameters must be constrained to match other types + = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters error[E0053]: method `call_once` has an incompatible type for trait --> $DIR/issue-20225.rs:18:3 diff --git a/src/test/ui/issues/issue-21332.stderr b/src/test/ui/issues/issue-21332.stderr index ace3e014647c..cc95b31acb7a 100644 --- a/src/test/ui/issues/issue-21332.stderr +++ b/src/test/ui/issues/issue-21332.stderr @@ -5,7 +5,7 @@ LL | fn next(&mut self) -> Result { Ok(7) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected enum `std::option::Option`, found enum `std::result::Result` | = note: expected fn pointer `fn(&mut S) -> std::option::Option` - found fn pointer `fn(&mut S) -> std::result::Result` + found fn pointer `fn(&'z0 mut S) -> std::result::Result` error: aborting due to previous error diff --git a/src/test/ui/lifetimes/lifetime-mismatch-between-trait-and-impl.stderr b/src/test/ui/lifetimes/lifetime-mismatch-between-trait-and-impl.stderr index d07f305954b6..57d145dcc20a 100644 --- a/src/test/ui/lifetimes/lifetime-mismatch-between-trait-and-impl.stderr +++ b/src/test/ui/lifetimes/lifetime-mismatch-between-trait-and-impl.stderr @@ -8,7 +8,9 @@ LL | fn foo<'a>(x: &'a i32, y: &'a i32) -> &'a i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found fn(&i32, &i32) -> &i32 | = note: expected `fn(&i32, &'a i32) -> &'a i32` - found `fn(&i32, &i32) -> &i32` + found `fn(&'z0 i32, &'z0 i32) -> &'z0 i32` + = note: the lifetime requirements from the `trait` could not be fulfilled by the `impl` + = help: consider adding a named lifetime to the `trait` that constrains the item's `self` argument, its inputs and its output with it error: aborting due to previous error diff --git a/src/test/ui/mismatched_types/E0053.stderr b/src/test/ui/mismatched_types/E0053.stderr index fef83e6bbe2b..4926068e583e 100644 --- a/src/test/ui/mismatched_types/E0053.stderr +++ b/src/test/ui/mismatched_types/E0053.stderr @@ -20,7 +20,7 @@ LL | fn bar(&mut self) { } | ^^^^^^^^^ types differ in mutability | = note: expected fn pointer `fn(&Bar)` - found fn pointer `fn(&mut Bar)` + found fn pointer `fn(&'z0 mut Bar)` help: consider change the type to match the mutability in trait | LL | fn bar(&self) { } diff --git a/src/test/ui/mismatched_types/trait-impl-fn-incompatibility.stderr b/src/test/ui/mismatched_types/trait-impl-fn-incompatibility.stderr index b20fddb05acf..debb371db510 100644 --- a/src/test/ui/mismatched_types/trait-impl-fn-incompatibility.stderr +++ b/src/test/ui/mismatched_types/trait-impl-fn-incompatibility.stderr @@ -20,7 +20,7 @@ LL | fn bar(&mut self, bar: &Bar) { } | ^^^^ types differ in mutability | = note: expected fn pointer `fn(&mut Bar, &mut Bar)` - found fn pointer `fn(&mut Bar, &Bar)` + found fn pointer `fn(&'z0 mut Bar, &'z1 Bar)` help: consider change the type to match the mutability in trait | LL | fn bar(&mut self, bar: &mut Bar) { } diff --git a/src/test/ui/nll/type-alias-free-regions.stderr b/src/test/ui/nll/type-alias-free-regions.stderr index 3317aae83bb0..d029cd23a8b2 100644 --- a/src/test/ui/nll/type-alias-free-regions.stderr +++ b/src/test/ui/nll/type-alias-free-regions.stderr @@ -16,7 +16,7 @@ note: ...so that the expression is assignable | LL | C { f: b } | ^ - = note: expected `std::boxed::Box>` + = note: expected `std::boxed::Box>` found `std::boxed::Box>` note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 15:6... --> $DIR/type-alias-free-regions.rs:15:6 @@ -49,7 +49,7 @@ note: ...so that the expression is assignable | LL | C { f: Box::new(b.0) } | ^^^ - = note: expected `std::boxed::Box<&isize>` + = note: expected `std::boxed::Box<&'z1 isize>` found `std::boxed::Box<&isize>` note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 25:6... --> $DIR/type-alias-free-regions.rs:25:6 diff --git a/src/test/ui/regions-fn-subtyping-return-static-fail.stderr b/src/test/ui/regions-fn-subtyping-return-static-fail.stderr index 27704b3e0a8c..074f78027c3b 100644 --- a/src/test/ui/regions-fn-subtyping-return-static-fail.stderr +++ b/src/test/ui/regions-fn-subtyping-return-static-fail.stderr @@ -5,7 +5,7 @@ LL | want_F(bar); | ^^^ expected concrete lifetime, found bound lifetime parameter 'cx | = note: expected fn pointer `for<'cx> fn(&'cx S) -> &'cx S` - found fn item `for<'a> fn(&'a S) -> &S {bar::<'_>}` + found fn item `for<'a> fn(&'a S) -> &'z2 S {bar::<'_>}` error[E0308]: mismatched types --> $DIR/regions-fn-subtyping-return-static-fail.rs:48:12 diff --git a/src/test/ui/regions/region-object-lifetime-in-coercion.stderr b/src/test/ui/regions/region-object-lifetime-in-coercion.stderr index 069b897603cb..cbe8d01f9772 100644 --- a/src/test/ui/regions/region-object-lifetime-in-coercion.stderr +++ b/src/test/ui/regions/region-object-lifetime-in-coercion.stderr @@ -39,7 +39,7 @@ note: ...so that the expression is assignable | LL | Box::new(v) | ^ - = note: expected `&[u8]` + = note: expected `&'z1 [u8]` found `&'a [u8]` note: but, the lifetime must be valid for the lifetime `'b` as defined on the function body at 25:9... --> $DIR/region-object-lifetime-in-coercion.rs:25:9 diff --git a/src/test/ui/regions/regions-fn-subtyping-return-static.stderr b/src/test/ui/regions/regions-fn-subtyping-return-static.stderr index a8a7e97e6acf..b26891a67898 100644 --- a/src/test/ui/regions/regions-fn-subtyping-return-static.stderr +++ b/src/test/ui/regions/regions-fn-subtyping-return-static.stderr @@ -5,7 +5,7 @@ LL | want_F(bar); | ^^^ expected concrete lifetime, found bound lifetime parameter 'cx | = note: expected fn pointer `for<'cx> fn(&'cx S) -> &'cx S` - found fn item `for<'a> fn(&'a S) -> &S {bar::<'_>}` + found fn item `for<'a> fn(&'a S) -> &'z2 S {bar::<'_>}` error: aborting due to previous error diff --git a/src/test/ui/regions/regions-nested-fns.stderr b/src/test/ui/regions/regions-nested-fns.stderr index 9e405d83140d..40e9146d6894 100644 --- a/src/test/ui/regions/regions-nested-fns.stderr +++ b/src/test/ui/regions/regions-nested-fns.stderr @@ -40,7 +40,7 @@ LL | | return z; LL | | })); | |_____^ = note: expected `&isize` - found `&isize` + found `&'z13 isize` error[E0312]: lifetime of reference outlives lifetime of borrowed content... --> $DIR/regions-nested-fns.rs:14:27 diff --git a/src/test/ui/regions/regions-ret-borrowed-1.stderr b/src/test/ui/regions/regions-ret-borrowed-1.stderr index 2c4769d8e375..b489290d9f71 100644 --- a/src/test/ui/regions/regions-ret-borrowed-1.stderr +++ b/src/test/ui/regions/regions-ret-borrowed-1.stderr @@ -14,7 +14,7 @@ note: ...so that the expression is assignable | LL | with(|o| o) | ^ - = note: expected `&isize` + = note: expected `&'z0 isize` found `&isize` note: but, the lifetime must be valid for the lifetime `'a` as defined on the function body at 9:14... --> $DIR/regions-ret-borrowed-1.rs:9:14 diff --git a/src/test/ui/regions/regions-ret-borrowed.stderr b/src/test/ui/regions/regions-ret-borrowed.stderr index da560107cea9..6b2a041b95bc 100644 --- a/src/test/ui/regions/regions-ret-borrowed.stderr +++ b/src/test/ui/regions/regions-ret-borrowed.stderr @@ -14,7 +14,7 @@ note: ...so that the expression is assignable | LL | with(|o| o) | ^ - = note: expected `&isize` + = note: expected `&'z0 isize` found `&isize` note: but, the lifetime must be valid for the lifetime `'a` as defined on the function body at 12:14... --> $DIR/regions-ret-borrowed.rs:12:14 diff --git a/src/test/ui/regions/regions-trait-1.stderr b/src/test/ui/regions/regions-trait-1.stderr index 60ac7c09f041..6b3c40658a73 100644 --- a/src/test/ui/regions/regions-trait-1.stderr +++ b/src/test/ui/regions/regions-trait-1.stderr @@ -5,7 +5,7 @@ LL | fn get_ctxt(&self) -> &'a Ctxt { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime mismatch | = note: expected fn pointer `fn(&HasCtxt<'a>) -> &Ctxt` - found fn pointer `fn(&HasCtxt<'a>) -> &'a Ctxt` + found fn pointer `fn(&'z0 HasCtxt<'a>) -> &'a Ctxt` note: the lifetime `'a` as defined on the impl at 12:6... --> $DIR/regions-trait-1.rs:12:6 | diff --git a/src/test/ui/regions/regions-trait-object-subtyping.stderr b/src/test/ui/regions/regions-trait-object-subtyping.stderr index 7478b53bd3cc..271849fdef69 100644 --- a/src/test/ui/regions/regions-trait-object-subtyping.stderr +++ b/src/test/ui/regions/regions-trait-object-subtyping.stderr @@ -42,7 +42,7 @@ note: ...so that the expression is assignable LL | x | ^ = note: expected `&'b mut (dyn Dummy + 'b)` - found `&mut (dyn Dummy + 'b)` + found `&'z1 mut (dyn Dummy + 'b)` error[E0308]: mismatched types --> $DIR/regions-trait-object-subtyping.rs:22:5 diff --git a/src/test/ui/resolve/resolve-inconsistent-binding-mode.stderr b/src/test/ui/resolve/resolve-inconsistent-binding-mode.stderr index c14dfa3601a8..0d19ea1ff250 100644 --- a/src/test/ui/resolve/resolve-inconsistent-binding-mode.stderr +++ b/src/test/ui/resolve/resolve-inconsistent-binding-mode.stderr @@ -54,8 +54,8 @@ LL | Opts::A(ref mut i) | Opts::B(ref i) => {} | | | first introduced with type `&mut isize` here | - = note: expected type `&mut isize` - found type `&isize` + = note: expected type `&'z0 mut isize` + found type `&'z1 isize` = note: in the same arm, a binding must have the same type in all alternatives error: aborting due to 6 previous errors diff --git a/src/test/ui/span/coerce-suggestions.stderr b/src/test/ui/span/coerce-suggestions.stderr index d1960a8aab30..8ea45e4191da 100644 --- a/src/test/ui/span/coerce-suggestions.stderr +++ b/src/test/ui/span/coerce-suggestions.stderr @@ -22,8 +22,8 @@ error[E0308]: mismatched types LL | test(&y); | ^^ types differ in mutability | - = note: expected mutable reference `&mut std::string::String` - found reference `&std::string::String` + = note: expected mutable reference `&'z2 mut std::string::String` + found reference `&'z3 std::string::String` error[E0308]: mismatched types --> $DIR/coerce-suggestions.rs:14:11 diff --git a/src/test/ui/traits/trait-impl-method-mismatch.stderr b/src/test/ui/traits/trait-impl-method-mismatch.stderr index 52e491862416..517da553dc0e 100644 --- a/src/test/ui/traits/trait-impl-method-mismatch.stderr +++ b/src/test/ui/traits/trait-impl-method-mismatch.stderr @@ -8,7 +8,7 @@ LL | unsafe fn jumbo(&self, x: &usize) { *self + *x; } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected normal fn, found unsafe fn | = note: expected fn pointer `fn(&usize, &usize) -> usize` - found fn pointer `unsafe fn(&usize, &usize)` + found fn pointer `unsafe fn(&'z0 usize, &'z1 usize)` error: aborting due to previous error diff --git a/src/test/ui/type/type-mismatch.stderr b/src/test/ui/type/type-mismatch.stderr index 24c71c63103d..7beffa0e5e49 100644 --- a/src/test/ui/type/type-mismatch.stderr +++ b/src/test/ui/type/type-mismatch.stderr @@ -211,7 +211,7 @@ LL | want::<&Foo>(f); | expected `&Foo`, found struct `Foo` | help: consider borrowing here: `&f` | - = note: expected reference `&Foo` + = note: expected reference `&'z0 Foo` found struct `Foo` error[E0308]: mismatched types @@ -313,7 +313,7 @@ LL | want::<&Foo>(f); | expected `&Foo`, found struct `Foo` | help: consider borrowing here: `&f` | - = note: expected reference `&Foo` + = note: expected reference `&'z1 Foo` found struct `Foo` error[E0308]: mismatched types diff --git a/src/test/ui/unsafe/unsafe-trait-impl.rs b/src/test/ui/unsafe/unsafe-trait-impl.rs index 03a251be1a91..691c751a080f 100644 --- a/src/test/ui/unsafe/unsafe-trait-impl.rs +++ b/src/test/ui/unsafe/unsafe-trait-impl.rs @@ -8,7 +8,7 @@ impl Foo for u32 { fn len(&self) -> u32 { *self } //~^ ERROR method `len` has an incompatible type for trait //~| expected fn pointer `unsafe fn(&u32) -> _` - //~| found fn pointer `fn(&u32) -> _` + //~| found fn pointer `fn(&'z0 u32) -> _` } fn main() { } diff --git a/src/test/ui/unsafe/unsafe-trait-impl.stderr b/src/test/ui/unsafe/unsafe-trait-impl.stderr index 1c3d057cbc9c..f163790eb451 100644 --- a/src/test/ui/unsafe/unsafe-trait-impl.stderr +++ b/src/test/ui/unsafe/unsafe-trait-impl.stderr @@ -8,7 +8,7 @@ LL | fn len(&self) -> u32 { *self } | ^^^^^^^^^^^^^^^^^^^^ expected unsafe fn, found normal fn | = note: expected fn pointer `unsafe fn(&u32) -> _` - found fn pointer `fn(&u32) -> _` + found fn pointer `fn(&'z0 u32) -> _` error: aborting due to previous error diff --git a/src/test/ui/wrong-mul-method-signature.stderr b/src/test/ui/wrong-mul-method-signature.stderr index 4c367fb9e9ca..cb901cdeedf0 100644 --- a/src/test/ui/wrong-mul-method-signature.stderr +++ b/src/test/ui/wrong-mul-method-signature.stderr @@ -5,7 +5,7 @@ LL | fn mul(self, s: &f64) -> Vec1 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `f64`, found `&f64` | = note: expected fn pointer `fn(Vec1, f64) -> Vec1` - found fn pointer `fn(Vec1, &f64) -> Vec1` + found fn pointer `fn(Vec1, &'z0 f64) -> Vec1` error[E0053]: method `mul` has an incompatible type for trait --> $DIR/wrong-mul-method-signature.rs:33:5 From eb0f4d51df3be5b149ec032d62e9431ba4faf038 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 22 Dec 2019 13:53:01 -0800 Subject: [PATCH 44/56] Tweak output for mismatched impl item Detect type parameter that might require lifetime constraint. Do not name `ReVar`s in expected/found output. Reword text suggesting to check the lifetimes. --- .../infer/error_reporting/mod.rs | 5 +- .../trait_impl_difference.rs | 51 +++++++++++++++---- src/test/ui/coercion/coerce-mut.rs | 4 +- src/test/ui/coercion/coerce-mut.stderr | 4 +- .../reordered-type-param.stderr | 2 +- src/test/ui/hrtb/hrtb-exists-forall-fn.stderr | 2 +- .../impl-generic-mismatch-ab.stderr | 2 +- src/test/ui/impl-trait/trait_type.stderr | 2 +- .../mismatched_trait_impl-2.stderr | 4 +- .../mismatched_trait_impl.nll.stderr | 2 + .../mismatched_trait_impl.stderr | 4 +- src/test/ui/issues/issue-13033.rs | 2 +- src/test/ui/issues/issue-13033.stderr | 2 +- src/test/ui/issues/issue-16683.stderr | 2 +- src/test/ui/issues/issue-17758.stderr | 2 +- src/test/ui/issues/issue-20225.stderr | 4 +- src/test/ui/issues/issue-21332.stderr | 2 +- ...ime-mismatch-between-trait-and-impl.stderr | 4 +- src/test/ui/mismatched_types/E0053.stderr | 2 +- .../trait-impl-fn-incompatibility.stderr | 2 +- .../ui/nll/type-alias-free-regions.stderr | 4 +- ...ons-fn-subtyping-return-static-fail.stderr | 2 +- .../region-object-lifetime-in-coercion.stderr | 2 +- .../regions-fn-subtyping-return-static.stderr | 2 +- src/test/ui/regions/regions-nested-fns.stderr | 2 +- .../ui/regions/regions-ret-borrowed-1.stderr | 2 +- .../ui/regions/regions-ret-borrowed.stderr | 2 +- src/test/ui/regions/regions-trait-1.stderr | 2 +- .../regions-trait-object-subtyping.stderr | 2 +- .../resolve-inconsistent-binding-mode.stderr | 4 +- src/test/ui/span/coerce-suggestions.stderr | 4 +- .../traits/trait-impl-method-mismatch.stderr | 2 +- ...trait-param-without-lifetime-constraint.rs | 20 ++++++++ ...t-param-without-lifetime-constraint.stderr | 18 +++++++ src/test/ui/type/type-mismatch.stderr | 4 +- src/test/ui/unsafe/unsafe-trait-impl.rs | 2 +- src/test/ui/unsafe/unsafe-trait-impl.stderr | 2 +- src/test/ui/wrong-mul-method-signature.stderr | 2 +- 38 files changed, 126 insertions(+), 56 deletions(-) create mode 100644 src/test/ui/traits/trait-param-without-lifetime-constraint.rs create mode 100644 src/test/ui/traits/trait-param-without-lifetime-constraint.stderr diff --git a/src/librustc_infer/infer/error_reporting/mod.rs b/src/librustc_infer/infer/error_reporting/mod.rs index 66e7c87f2c35..ae9019828170 100644 --- a/src/librustc_infer/infer/error_reporting/mod.rs +++ b/src/librustc_infer/infer/error_reporting/mod.rs @@ -993,10 +993,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { s: &mut DiagnosticStyledString, ) { let mut r = region.to_string(); - if let ty::RegionKind::ReVar(var) = region { - // Show these named, not as `'_` or elide them in "expected/found" notes. - r = format!("'z{} ", var.index()); - } else if r == "'_" { + if r == "'_" { r.clear(); } else { r.push(' '); diff --git a/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs b/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs index 2e54a4e28e98..5aa55d253aa1 100644 --- a/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs +++ b/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs @@ -1,5 +1,6 @@ //! Error Reporting for `impl` items that do not match the obligations from their `trait`. +use crate::hir; use crate::hir::def_id::DefId; use crate::infer::error_reporting::nice_region_error::NiceRegionError; use crate::infer::lexical_region_resolve::RegionResolutionError; @@ -40,7 +41,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { var_origin.span(), sub_expected_found.expected, sub_expected_found.found, - self.tcx().def_span(*trait_item_def_id), + *trait_item_def_id, ); return Some(ErrorReported); } @@ -51,23 +52,56 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { None } - fn emit_err(&self, sp: Span, expected: Ty<'tcx>, found: Ty<'tcx>, impl_sp: Span) { + fn emit_err(&self, sp: Span, expected: Ty<'tcx>, found: Ty<'tcx>, trait_def_id: DefId) { + let tcx = self.tcx(); + let trait_sp = self.tcx().def_span(trait_def_id); let mut err = self .tcx() .sess .struct_span_err(sp, "`impl` item signature doesn't match `trait` item signature"); err.span_label(sp, &format!("found {:?}", found)); - err.span_label(impl_sp, &format!("expected {:?}", expected)); + err.span_label(trait_sp, &format!("expected {:?}", expected)); + let trait_fn_sig = tcx.fn_sig(trait_def_id); + + struct AssocTypeFinder(FxHashSet); + impl<'tcx> ty::fold::TypeVisitor<'tcx> for AssocTypeFinder { + fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { + debug!("assoc type finder ty {:?} {:?}", ty, ty.kind); + match ty.kind { + ty::Param(param) => { + self.0.insert(param); + } + _ => {} + } + ty.super_visit_with(self) + } + } + let mut visitor = AssocTypeFinder(FxHashSet::default()); + trait_fn_sig.output().visit_with(&mut visitor); + + if let Some(id) = tcx.hir().as_local_hir_id(trait_def_id) { + let parent_id = tcx.hir().get_parent_item(id); + let trait_item = tcx.hir().expect_item(parent_id); + if let hir::ItemKind::Trait(_, _, generics, _, _) = &trait_item.kind { + for param_ty in visitor.0 { + if let Some(generic) = generics.get_named(param_ty.name) { + err.span_label(generic.span, &format!( + "in order for `impl` items to be able to implement the method, this \ + type parameter might need a lifetime restriction like `{}: 'a`", + param_ty.name, + )); + } + } + } + } struct EarlyBoundRegionHighlighter(FxHashSet); impl<'tcx> ty::fold::TypeVisitor<'tcx> for EarlyBoundRegionHighlighter { fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool { - debug!("LateBoundRegionNameCollector visit_region {:?}", r); match *r { ty::ReFree(free) => { self.0.insert(free.scope); } - ty::ReEarlyBound(bound) => { self.0.insert(bound.def_id); } @@ -94,12 +128,11 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { } if note { err.note( - "the lifetime requirements from the `trait` could not be fulfilled by the \ - `impl`", + "the lifetime requirements from the `trait` could not be fulfilled by the `impl`", ); err.help( - "consider adding a named lifetime to the `trait` that constrains the item's \ - `self` argument, its inputs and its output with it", + "verify the lifetime relationships in the `trait` and `impl` between the \ + `self` argument, the other inputs and its output", ); } err.emit(); diff --git a/src/test/ui/coercion/coerce-mut.rs b/src/test/ui/coercion/coerce-mut.rs index 3ccfe1cede7a..43f0b55856d3 100644 --- a/src/test/ui/coercion/coerce-mut.rs +++ b/src/test/ui/coercion/coerce-mut.rs @@ -4,7 +4,7 @@ fn main() { let x = 0; f(&x); //~^ ERROR mismatched types - //~| expected mutable reference `&'z1 mut i32` - //~| found reference `&'z2 {integer}` + //~| expected mutable reference `&mut i32` + //~| found reference `&{integer}` //~| types differ in mutability } diff --git a/src/test/ui/coercion/coerce-mut.stderr b/src/test/ui/coercion/coerce-mut.stderr index f5b13f6c5909..2601ca5e91e5 100644 --- a/src/test/ui/coercion/coerce-mut.stderr +++ b/src/test/ui/coercion/coerce-mut.stderr @@ -4,8 +4,8 @@ error[E0308]: mismatched types LL | f(&x); | ^^ types differ in mutability | - = note: expected mutable reference `&'z1 mut i32` - found reference `&'z2 {integer}` + = note: expected mutable reference `&mut i32` + found reference `&{integer}` error: aborting due to previous error diff --git a/src/test/ui/compare-method/reordered-type-param.stderr b/src/test/ui/compare-method/reordered-type-param.stderr index 9eed16fa994e..f1f8a663f212 100644 --- a/src/test/ui/compare-method/reordered-type-param.stderr +++ b/src/test/ui/compare-method/reordered-type-param.stderr @@ -11,7 +11,7 @@ LL | fn b(&self, _x: G) -> G { panic!() } | expected type parameter | = note: expected fn pointer `fn(&E, F) -> F` - found fn pointer `fn(&'z0 E, G) -> G` + found fn pointer `fn(&E, G) -> G` = note: a type parameter was expected, but a different one was found; you might be missing a type parameter or trait bound = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters diff --git a/src/test/ui/hrtb/hrtb-exists-forall-fn.stderr b/src/test/ui/hrtb/hrtb-exists-forall-fn.stderr index 4d09fe74e16f..328e98657eff 100644 --- a/src/test/ui/hrtb/hrtb-exists-forall-fn.stderr +++ b/src/test/ui/hrtb/hrtb-exists-forall-fn.stderr @@ -7,7 +7,7 @@ LL | let _: for<'b> fn(&'b u32) = foo(); | expected due to this | = note: expected fn pointer `for<'b> fn(&'b u32)` - found fn pointer `fn(&'z0 u32)` + found fn pointer `fn(&u32)` error: aborting due to previous error diff --git a/src/test/ui/impl-trait/impl-generic-mismatch-ab.stderr b/src/test/ui/impl-trait/impl-generic-mismatch-ab.stderr index d5725efbac3c..638a0093fb21 100644 --- a/src/test/ui/impl-trait/impl-generic-mismatch-ab.stderr +++ b/src/test/ui/impl-trait/impl-generic-mismatch-ab.stderr @@ -10,7 +10,7 @@ LL | fn foo(&self, a: &impl Debug, b: &B) { } | expected type parameter | = note: expected fn pointer `fn(&(), &B, &impl Debug)` - found fn pointer `fn(&'z0 (), &impl Debug, &B)` + found fn pointer `fn(&(), &impl Debug, &B)` = note: a type parameter was expected, but a different one was found; you might be missing a type parameter or trait bound = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters diff --git a/src/test/ui/impl-trait/trait_type.stderr b/src/test/ui/impl-trait/trait_type.stderr index 3cc9c1a7fb05..748bc639a03c 100644 --- a/src/test/ui/impl-trait/trait_type.stderr +++ b/src/test/ui/impl-trait/trait_type.stderr @@ -5,7 +5,7 @@ LL | fn fmt(&self, x: &str) -> () { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ in mutability | = note: expected fn pointer `fn(&MyType, &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error>` - found fn pointer `fn(&'z0 MyType, &str)` + found fn pointer `fn(&MyType, &str)` error[E0050]: method `fmt` has 1 parameter but the declaration in trait `std::fmt::Display::fmt` has 2 --> $DIR/trait_type.rs:12:11 diff --git a/src/test/ui/in-band-lifetimes/mismatched_trait_impl-2.stderr b/src/test/ui/in-band-lifetimes/mismatched_trait_impl-2.stderr index 349f813b20d5..d8a2fa14333e 100644 --- a/src/test/ui/in-band-lifetimes/mismatched_trait_impl-2.stderr +++ b/src/test/ui/in-band-lifetimes/mismatched_trait_impl-2.stderr @@ -10,9 +10,9 @@ LL | fn deref(&self) -> &Self::Target; | --------------------------------- expected fn(&Struct) -> &(dyn Trait + 'static) | = note: expected `fn(&Struct) -> &(dyn Trait + 'static)` - found `fn(&'z0 Struct) -> &dyn Trait` + found `fn(&Struct) -> &dyn Trait` = note: the lifetime requirements from the `trait` could not be fulfilled by the `impl` - = help: consider adding a named lifetime to the `trait` that constrains the item's `self` argument, its inputs and its output with it + = help: verify the lifetime relationships in the `trait` and `impl` between the `self` argument, the other inputs and its output error: aborting due to previous error diff --git a/src/test/ui/in-band-lifetimes/mismatched_trait_impl.nll.stderr b/src/test/ui/in-band-lifetimes/mismatched_trait_impl.nll.stderr index c245d78ae828..88edf10a66f0 100644 --- a/src/test/ui/in-band-lifetimes/mismatched_trait_impl.nll.stderr +++ b/src/test/ui/in-band-lifetimes/mismatched_trait_impl.nll.stderr @@ -9,6 +9,8 @@ LL | fn foo(&self, x: &u32, y: &'a u32) -> &'a u32 { | = note: expected `fn(&i32, &'a u32, &u32) -> &'a u32` found `fn(&i32, &u32, &u32) -> &u32` + = note: the lifetime requirements from the `trait` could not be fulfilled by the `impl` + = help: verify the lifetime relationships in the `trait` and `impl` between the `self` argument, the other inputs and its output error: aborting due to previous error diff --git a/src/test/ui/in-band-lifetimes/mismatched_trait_impl.stderr b/src/test/ui/in-band-lifetimes/mismatched_trait_impl.stderr index 72960a41f93d..dcdc6d330c0e 100644 --- a/src/test/ui/in-band-lifetimes/mismatched_trait_impl.stderr +++ b/src/test/ui/in-band-lifetimes/mismatched_trait_impl.stderr @@ -8,9 +8,9 @@ LL | fn foo(&self, x: &u32, y: &'a u32) -> &'a u32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found fn(&i32, &u32, &u32) -> &u32 | = note: expected `fn(&i32, &'a u32, &u32) -> &'a u32` - found `fn(&'z0 i32, &'z1 u32, &'z2 u32) -> &'z2 u32` + found `fn(&i32, &u32, &u32) -> &u32` = note: the lifetime requirements from the `trait` could not be fulfilled by the `impl` - = help: consider adding a named lifetime to the `trait` that constrains the item's `self` argument, its inputs and its output with it + = help: verify the lifetime relationships in the `trait` and `impl` between the `self` argument, the other inputs and its output error[E0623]: lifetime mismatch --> $DIR/mismatched_trait_impl.rs:10:9 diff --git a/src/test/ui/issues/issue-13033.rs b/src/test/ui/issues/issue-13033.rs index 3fa94fac2375..7631831a81a5 100644 --- a/src/test/ui/issues/issue-13033.rs +++ b/src/test/ui/issues/issue-13033.rs @@ -8,7 +8,7 @@ impl Foo for Baz { fn bar(&mut self, other: &dyn Foo) {} //~^ ERROR method `bar` has an incompatible type for trait //~| expected fn pointer `fn(&mut Baz, &mut dyn Foo)` - //~| found fn pointer `fn(&'z0 mut Baz, &dyn Foo)` + //~| found fn pointer `fn(&mut Baz, &dyn Foo)` } fn main() {} diff --git a/src/test/ui/issues/issue-13033.stderr b/src/test/ui/issues/issue-13033.stderr index 238f41782ba5..a8473c8a5241 100644 --- a/src/test/ui/issues/issue-13033.stderr +++ b/src/test/ui/issues/issue-13033.stderr @@ -8,7 +8,7 @@ LL | fn bar(&mut self, other: &dyn Foo) {} | ^^^^^^^^ types differ in mutability | = note: expected fn pointer `fn(&mut Baz, &mut dyn Foo)` - found fn pointer `fn(&'z0 mut Baz, &dyn Foo)` + found fn pointer `fn(&mut Baz, &dyn Foo)` help: consider change the type to match the mutability in trait | LL | fn bar(&mut self, other: &mut dyn Foo) {} diff --git a/src/test/ui/issues/issue-16683.stderr b/src/test/ui/issues/issue-16683.stderr index fa782d043312..4f6583307581 100644 --- a/src/test/ui/issues/issue-16683.stderr +++ b/src/test/ui/issues/issue-16683.stderr @@ -27,7 +27,7 @@ note: ...so that the types are compatible LL | self.a(); | ^ = note: expected `&'a Self` - found `&'z0 Self` + found `&Self` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-17758.stderr b/src/test/ui/issues/issue-17758.stderr index 7c382b95ae9a..31788cfa61c4 100644 --- a/src/test/ui/issues/issue-17758.stderr +++ b/src/test/ui/issues/issue-17758.stderr @@ -28,7 +28,7 @@ note: ...so that the types are compatible LL | self.foo(); | ^^^ = note: expected `&'a Self` - found `&'z0 Self` + found `&Self` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-20225.stderr b/src/test/ui/issues/issue-20225.stderr index 351973d444b0..133dbc554c08 100644 --- a/src/test/ui/issues/issue-20225.stderr +++ b/src/test/ui/issues/issue-20225.stderr @@ -7,7 +7,7 @@ LL | extern "rust-call" fn call(&self, (_,): (T,)) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&T`, found type parameter `T` | = note: expected fn pointer `extern "rust-call" fn(&Foo, (&'a T,))` - found fn pointer `extern "rust-call" fn(&'z0 Foo, (T,))` + found fn pointer `extern "rust-call" fn(&Foo, (T,))` = help: type parameters must be constrained to match other types = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters @@ -20,7 +20,7 @@ LL | extern "rust-call" fn call_mut(&mut self, (_,): (T,)) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&T`, found type parameter `T` | = note: expected fn pointer `extern "rust-call" fn(&mut Foo, (&'a T,))` - found fn pointer `extern "rust-call" fn(&'z0 mut Foo, (T,))` + found fn pointer `extern "rust-call" fn(&mut Foo, (T,))` = help: type parameters must be constrained to match other types = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters diff --git a/src/test/ui/issues/issue-21332.stderr b/src/test/ui/issues/issue-21332.stderr index cc95b31acb7a..ace3e014647c 100644 --- a/src/test/ui/issues/issue-21332.stderr +++ b/src/test/ui/issues/issue-21332.stderr @@ -5,7 +5,7 @@ LL | fn next(&mut self) -> Result { Ok(7) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected enum `std::option::Option`, found enum `std::result::Result` | = note: expected fn pointer `fn(&mut S) -> std::option::Option` - found fn pointer `fn(&'z0 mut S) -> std::result::Result` + found fn pointer `fn(&mut S) -> std::result::Result` error: aborting due to previous error diff --git a/src/test/ui/lifetimes/lifetime-mismatch-between-trait-and-impl.stderr b/src/test/ui/lifetimes/lifetime-mismatch-between-trait-and-impl.stderr index 57d145dcc20a..fa2ea83deefb 100644 --- a/src/test/ui/lifetimes/lifetime-mismatch-between-trait-and-impl.stderr +++ b/src/test/ui/lifetimes/lifetime-mismatch-between-trait-and-impl.stderr @@ -8,9 +8,9 @@ LL | fn foo<'a>(x: &'a i32, y: &'a i32) -> &'a i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found fn(&i32, &i32) -> &i32 | = note: expected `fn(&i32, &'a i32) -> &'a i32` - found `fn(&'z0 i32, &'z0 i32) -> &'z0 i32` + found `fn(&i32, &i32) -> &i32` = note: the lifetime requirements from the `trait` could not be fulfilled by the `impl` - = help: consider adding a named lifetime to the `trait` that constrains the item's `self` argument, its inputs and its output with it + = help: verify the lifetime relationships in the `trait` and `impl` between the `self` argument, the other inputs and its output error: aborting due to previous error diff --git a/src/test/ui/mismatched_types/E0053.stderr b/src/test/ui/mismatched_types/E0053.stderr index 4926068e583e..fef83e6bbe2b 100644 --- a/src/test/ui/mismatched_types/E0053.stderr +++ b/src/test/ui/mismatched_types/E0053.stderr @@ -20,7 +20,7 @@ LL | fn bar(&mut self) { } | ^^^^^^^^^ types differ in mutability | = note: expected fn pointer `fn(&Bar)` - found fn pointer `fn(&'z0 mut Bar)` + found fn pointer `fn(&mut Bar)` help: consider change the type to match the mutability in trait | LL | fn bar(&self) { } diff --git a/src/test/ui/mismatched_types/trait-impl-fn-incompatibility.stderr b/src/test/ui/mismatched_types/trait-impl-fn-incompatibility.stderr index debb371db510..b20fddb05acf 100644 --- a/src/test/ui/mismatched_types/trait-impl-fn-incompatibility.stderr +++ b/src/test/ui/mismatched_types/trait-impl-fn-incompatibility.stderr @@ -20,7 +20,7 @@ LL | fn bar(&mut self, bar: &Bar) { } | ^^^^ types differ in mutability | = note: expected fn pointer `fn(&mut Bar, &mut Bar)` - found fn pointer `fn(&'z0 mut Bar, &'z1 Bar)` + found fn pointer `fn(&mut Bar, &Bar)` help: consider change the type to match the mutability in trait | LL | fn bar(&mut self, bar: &mut Bar) { } diff --git a/src/test/ui/nll/type-alias-free-regions.stderr b/src/test/ui/nll/type-alias-free-regions.stderr index d029cd23a8b2..3317aae83bb0 100644 --- a/src/test/ui/nll/type-alias-free-regions.stderr +++ b/src/test/ui/nll/type-alias-free-regions.stderr @@ -16,7 +16,7 @@ note: ...so that the expression is assignable | LL | C { f: b } | ^ - = note: expected `std::boxed::Box>` + = note: expected `std::boxed::Box>` found `std::boxed::Box>` note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 15:6... --> $DIR/type-alias-free-regions.rs:15:6 @@ -49,7 +49,7 @@ note: ...so that the expression is assignable | LL | C { f: Box::new(b.0) } | ^^^ - = note: expected `std::boxed::Box<&'z1 isize>` + = note: expected `std::boxed::Box<&isize>` found `std::boxed::Box<&isize>` note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 25:6... --> $DIR/type-alias-free-regions.rs:25:6 diff --git a/src/test/ui/regions-fn-subtyping-return-static-fail.stderr b/src/test/ui/regions-fn-subtyping-return-static-fail.stderr index 074f78027c3b..27704b3e0a8c 100644 --- a/src/test/ui/regions-fn-subtyping-return-static-fail.stderr +++ b/src/test/ui/regions-fn-subtyping-return-static-fail.stderr @@ -5,7 +5,7 @@ LL | want_F(bar); | ^^^ expected concrete lifetime, found bound lifetime parameter 'cx | = note: expected fn pointer `for<'cx> fn(&'cx S) -> &'cx S` - found fn item `for<'a> fn(&'a S) -> &'z2 S {bar::<'_>}` + found fn item `for<'a> fn(&'a S) -> &S {bar::<'_>}` error[E0308]: mismatched types --> $DIR/regions-fn-subtyping-return-static-fail.rs:48:12 diff --git a/src/test/ui/regions/region-object-lifetime-in-coercion.stderr b/src/test/ui/regions/region-object-lifetime-in-coercion.stderr index cbe8d01f9772..069b897603cb 100644 --- a/src/test/ui/regions/region-object-lifetime-in-coercion.stderr +++ b/src/test/ui/regions/region-object-lifetime-in-coercion.stderr @@ -39,7 +39,7 @@ note: ...so that the expression is assignable | LL | Box::new(v) | ^ - = note: expected `&'z1 [u8]` + = note: expected `&[u8]` found `&'a [u8]` note: but, the lifetime must be valid for the lifetime `'b` as defined on the function body at 25:9... --> $DIR/region-object-lifetime-in-coercion.rs:25:9 diff --git a/src/test/ui/regions/regions-fn-subtyping-return-static.stderr b/src/test/ui/regions/regions-fn-subtyping-return-static.stderr index b26891a67898..a8a7e97e6acf 100644 --- a/src/test/ui/regions/regions-fn-subtyping-return-static.stderr +++ b/src/test/ui/regions/regions-fn-subtyping-return-static.stderr @@ -5,7 +5,7 @@ LL | want_F(bar); | ^^^ expected concrete lifetime, found bound lifetime parameter 'cx | = note: expected fn pointer `for<'cx> fn(&'cx S) -> &'cx S` - found fn item `for<'a> fn(&'a S) -> &'z2 S {bar::<'_>}` + found fn item `for<'a> fn(&'a S) -> &S {bar::<'_>}` error: aborting due to previous error diff --git a/src/test/ui/regions/regions-nested-fns.stderr b/src/test/ui/regions/regions-nested-fns.stderr index 40e9146d6894..9e405d83140d 100644 --- a/src/test/ui/regions/regions-nested-fns.stderr +++ b/src/test/ui/regions/regions-nested-fns.stderr @@ -40,7 +40,7 @@ LL | | return z; LL | | })); | |_____^ = note: expected `&isize` - found `&'z13 isize` + found `&isize` error[E0312]: lifetime of reference outlives lifetime of borrowed content... --> $DIR/regions-nested-fns.rs:14:27 diff --git a/src/test/ui/regions/regions-ret-borrowed-1.stderr b/src/test/ui/regions/regions-ret-borrowed-1.stderr index b489290d9f71..2c4769d8e375 100644 --- a/src/test/ui/regions/regions-ret-borrowed-1.stderr +++ b/src/test/ui/regions/regions-ret-borrowed-1.stderr @@ -14,7 +14,7 @@ note: ...so that the expression is assignable | LL | with(|o| o) | ^ - = note: expected `&'z0 isize` + = note: expected `&isize` found `&isize` note: but, the lifetime must be valid for the lifetime `'a` as defined on the function body at 9:14... --> $DIR/regions-ret-borrowed-1.rs:9:14 diff --git a/src/test/ui/regions/regions-ret-borrowed.stderr b/src/test/ui/regions/regions-ret-borrowed.stderr index 6b2a041b95bc..da560107cea9 100644 --- a/src/test/ui/regions/regions-ret-borrowed.stderr +++ b/src/test/ui/regions/regions-ret-borrowed.stderr @@ -14,7 +14,7 @@ note: ...so that the expression is assignable | LL | with(|o| o) | ^ - = note: expected `&'z0 isize` + = note: expected `&isize` found `&isize` note: but, the lifetime must be valid for the lifetime `'a` as defined on the function body at 12:14... --> $DIR/regions-ret-borrowed.rs:12:14 diff --git a/src/test/ui/regions/regions-trait-1.stderr b/src/test/ui/regions/regions-trait-1.stderr index 6b3c40658a73..60ac7c09f041 100644 --- a/src/test/ui/regions/regions-trait-1.stderr +++ b/src/test/ui/regions/regions-trait-1.stderr @@ -5,7 +5,7 @@ LL | fn get_ctxt(&self) -> &'a Ctxt { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime mismatch | = note: expected fn pointer `fn(&HasCtxt<'a>) -> &Ctxt` - found fn pointer `fn(&'z0 HasCtxt<'a>) -> &'a Ctxt` + found fn pointer `fn(&HasCtxt<'a>) -> &'a Ctxt` note: the lifetime `'a` as defined on the impl at 12:6... --> $DIR/regions-trait-1.rs:12:6 | diff --git a/src/test/ui/regions/regions-trait-object-subtyping.stderr b/src/test/ui/regions/regions-trait-object-subtyping.stderr index 271849fdef69..7478b53bd3cc 100644 --- a/src/test/ui/regions/regions-trait-object-subtyping.stderr +++ b/src/test/ui/regions/regions-trait-object-subtyping.stderr @@ -42,7 +42,7 @@ note: ...so that the expression is assignable LL | x | ^ = note: expected `&'b mut (dyn Dummy + 'b)` - found `&'z1 mut (dyn Dummy + 'b)` + found `&mut (dyn Dummy + 'b)` error[E0308]: mismatched types --> $DIR/regions-trait-object-subtyping.rs:22:5 diff --git a/src/test/ui/resolve/resolve-inconsistent-binding-mode.stderr b/src/test/ui/resolve/resolve-inconsistent-binding-mode.stderr index 0d19ea1ff250..c14dfa3601a8 100644 --- a/src/test/ui/resolve/resolve-inconsistent-binding-mode.stderr +++ b/src/test/ui/resolve/resolve-inconsistent-binding-mode.stderr @@ -54,8 +54,8 @@ LL | Opts::A(ref mut i) | Opts::B(ref i) => {} | | | first introduced with type `&mut isize` here | - = note: expected type `&'z0 mut isize` - found type `&'z1 isize` + = note: expected type `&mut isize` + found type `&isize` = note: in the same arm, a binding must have the same type in all alternatives error: aborting due to 6 previous errors diff --git a/src/test/ui/span/coerce-suggestions.stderr b/src/test/ui/span/coerce-suggestions.stderr index 8ea45e4191da..d1960a8aab30 100644 --- a/src/test/ui/span/coerce-suggestions.stderr +++ b/src/test/ui/span/coerce-suggestions.stderr @@ -22,8 +22,8 @@ error[E0308]: mismatched types LL | test(&y); | ^^ types differ in mutability | - = note: expected mutable reference `&'z2 mut std::string::String` - found reference `&'z3 std::string::String` + = note: expected mutable reference `&mut std::string::String` + found reference `&std::string::String` error[E0308]: mismatched types --> $DIR/coerce-suggestions.rs:14:11 diff --git a/src/test/ui/traits/trait-impl-method-mismatch.stderr b/src/test/ui/traits/trait-impl-method-mismatch.stderr index 517da553dc0e..52e491862416 100644 --- a/src/test/ui/traits/trait-impl-method-mismatch.stderr +++ b/src/test/ui/traits/trait-impl-method-mismatch.stderr @@ -8,7 +8,7 @@ LL | unsafe fn jumbo(&self, x: &usize) { *self + *x; } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected normal fn, found unsafe fn | = note: expected fn pointer `fn(&usize, &usize) -> usize` - found fn pointer `unsafe fn(&'z0 usize, &'z1 usize)` + found fn pointer `unsafe fn(&usize, &usize)` error: aborting due to previous error diff --git a/src/test/ui/traits/trait-param-without-lifetime-constraint.rs b/src/test/ui/traits/trait-param-without-lifetime-constraint.rs new file mode 100644 index 000000000000..a79b74dcddea --- /dev/null +++ b/src/test/ui/traits/trait-param-without-lifetime-constraint.rs @@ -0,0 +1,20 @@ +struct Article { + proof_reader: ProofReader, +} + +struct ProofReader { + name: String, +} + +pub trait HaveRelationship { + fn get_relation(&self) -> To; +} + +impl HaveRelationship<&ProofReader> for Article { + fn get_relation(&self) -> &ProofReader { + //~^ ERROR `impl` item signature doesn't match `trait` item signature + &self.proof_reader + } +} + +fn main() {} diff --git a/src/test/ui/traits/trait-param-without-lifetime-constraint.stderr b/src/test/ui/traits/trait-param-without-lifetime-constraint.stderr new file mode 100644 index 000000000000..912d3a8d28de --- /dev/null +++ b/src/test/ui/traits/trait-param-without-lifetime-constraint.stderr @@ -0,0 +1,18 @@ +error: `impl` item signature doesn't match `trait` item signature + --> $DIR/trait-param-without-lifetime-constraint.rs:14:5 + | +LL | pub trait HaveRelationship { + | -- in order for `impl` items to be able to implement the method, this type parameter might need a lifetime restriction like `To: 'a` +LL | fn get_relation(&self) -> To; + | ----------------------------- expected fn(&Article) -> &ProofReader +... +LL | fn get_relation(&self) -> &ProofReader { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found fn(&Article) -> &ProofReader + | + = note: expected `fn(&Article) -> &ProofReader` + found `fn(&Article) -> &ProofReader` + = note: the lifetime requirements from the `trait` could not be fulfilled by the `impl` + = help: verify the lifetime relationships in the `trait` and `impl` between the `self` argument, the other inputs and its output + +error: aborting due to previous error + diff --git a/src/test/ui/type/type-mismatch.stderr b/src/test/ui/type/type-mismatch.stderr index 7beffa0e5e49..24c71c63103d 100644 --- a/src/test/ui/type/type-mismatch.stderr +++ b/src/test/ui/type/type-mismatch.stderr @@ -211,7 +211,7 @@ LL | want::<&Foo>(f); | expected `&Foo`, found struct `Foo` | help: consider borrowing here: `&f` | - = note: expected reference `&'z0 Foo` + = note: expected reference `&Foo` found struct `Foo` error[E0308]: mismatched types @@ -313,7 +313,7 @@ LL | want::<&Foo>(f); | expected `&Foo`, found struct `Foo` | help: consider borrowing here: `&f` | - = note: expected reference `&'z1 Foo` + = note: expected reference `&Foo` found struct `Foo` error[E0308]: mismatched types diff --git a/src/test/ui/unsafe/unsafe-trait-impl.rs b/src/test/ui/unsafe/unsafe-trait-impl.rs index 691c751a080f..03a251be1a91 100644 --- a/src/test/ui/unsafe/unsafe-trait-impl.rs +++ b/src/test/ui/unsafe/unsafe-trait-impl.rs @@ -8,7 +8,7 @@ impl Foo for u32 { fn len(&self) -> u32 { *self } //~^ ERROR method `len` has an incompatible type for trait //~| expected fn pointer `unsafe fn(&u32) -> _` - //~| found fn pointer `fn(&'z0 u32) -> _` + //~| found fn pointer `fn(&u32) -> _` } fn main() { } diff --git a/src/test/ui/unsafe/unsafe-trait-impl.stderr b/src/test/ui/unsafe/unsafe-trait-impl.stderr index f163790eb451..1c3d057cbc9c 100644 --- a/src/test/ui/unsafe/unsafe-trait-impl.stderr +++ b/src/test/ui/unsafe/unsafe-trait-impl.stderr @@ -8,7 +8,7 @@ LL | fn len(&self) -> u32 { *self } | ^^^^^^^^^^^^^^^^^^^^ expected unsafe fn, found normal fn | = note: expected fn pointer `unsafe fn(&u32) -> _` - found fn pointer `fn(&'z0 u32) -> _` + found fn pointer `fn(&u32) -> _` error: aborting due to previous error diff --git a/src/test/ui/wrong-mul-method-signature.stderr b/src/test/ui/wrong-mul-method-signature.stderr index cb901cdeedf0..4c367fb9e9ca 100644 --- a/src/test/ui/wrong-mul-method-signature.stderr +++ b/src/test/ui/wrong-mul-method-signature.stderr @@ -5,7 +5,7 @@ LL | fn mul(self, s: &f64) -> Vec1 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `f64`, found `&f64` | = note: expected fn pointer `fn(Vec1, f64) -> Vec1` - found fn pointer `fn(Vec1, &'z0 f64) -> Vec1` + found fn pointer `fn(Vec1, &f64) -> Vec1` error[E0053]: method `mul` has an incompatible type for trait --> $DIR/wrong-mul-method-signature.rs:33:5 From 38112321908e824a56943f8e2a461510206e2409 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 22 Dec 2019 17:53:50 -0800 Subject: [PATCH 45/56] review comments --- .../trait_impl_difference.rs | 58 ++++++------------- .../mismatched_trait_impl-2.stderr | 2 +- .../mismatched_trait_impl.nll.stderr | 2 +- .../mismatched_trait_impl.stderr | 2 +- ...ime-mismatch-between-trait-and-impl.stderr | 2 +- ...t-param-without-lifetime-constraint.stderr | 4 +- 6 files changed, 24 insertions(+), 46 deletions(-) diff --git a/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs b/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs index 5aa55d253aa1..7ad7e8897df3 100644 --- a/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs +++ b/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs @@ -63,22 +63,25 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { err.span_label(trait_sp, &format!("expected {:?}", expected)); let trait_fn_sig = tcx.fn_sig(trait_def_id); + // Check the `trait`'s method's output to look for type parameters that might have + // unconstrained lifetimes. If the method returns a type parameter and the `impl` has a + // borrow as the type parameter being implemented, the lifetimes will not match because + // a new lifetime is being introduced in the `impl` that is not present in the `trait`. + // Because this is confusing as hell the first time you see it, we give a short message + // explaining the situation and proposing constraining the type param with a named lifetime + // so that the `impl` will have one to tie them together. struct AssocTypeFinder(FxHashSet); impl<'tcx> ty::fold::TypeVisitor<'tcx> for AssocTypeFinder { fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { debug!("assoc type finder ty {:?} {:?}", ty, ty.kind); - match ty.kind { - ty::Param(param) => { - self.0.insert(param); - } - _ => {} + if let ty::Param(param) = ty.kind { + self.0.insert(param); } ty.super_visit_with(self) } } let mut visitor = AssocTypeFinder(FxHashSet::default()); trait_fn_sig.output().visit_with(&mut visitor); - if let Some(id) = tcx.hir().as_local_hir_id(trait_def_id) { let parent_id = tcx.hir().get_parent_item(id); let trait_item = tcx.hir().expect_item(parent_id); @@ -86,8 +89,8 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { for param_ty in visitor.0 { if let Some(generic) = generics.get_named(param_ty.name) { err.span_label(generic.span, &format!( - "in order for `impl` items to be able to implement the method, this \ - type parameter might need a lifetime restriction like `{}: 'a`", + "for `impl` items to implement the method, this type parameter might \ + need a lifetime restriction like `{}: 'a`", param_ty.name, )); } @@ -95,46 +98,21 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { } } - struct EarlyBoundRegionHighlighter(FxHashSet); - impl<'tcx> ty::fold::TypeVisitor<'tcx> for EarlyBoundRegionHighlighter { - fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool { - match *r { - ty::ReFree(free) => { - self.0.insert(free.scope); - } - ty::ReEarlyBound(bound) => { - self.0.insert(bound.def_id); - } - _ => {} - } - r.super_visit_with(self) - } - } - - let mut visitor = EarlyBoundRegionHighlighter(FxHashSet::default()); - expected.visit_with(&mut visitor); - - let note = !visitor.0.is_empty(); - - if let Some((expected, found)) = self - .tcx() + if let Some((expected, found)) = tcx .infer_ctxt() .enter(|infcx| infcx.expected_found_str_ty(&ExpectedFound { expected, found })) { + // Highlighted the differences when showing the "expected/found" note. err.note_expected_found(&"", expected, &"", found); } else { // This fallback shouldn't be necessary, but let's keep it in just in case. err.note(&format!("expected `{:?}`\n found `{:?}`", expected, found)); } - if note { - err.note( - "the lifetime requirements from the `trait` could not be fulfilled by the `impl`", - ); - err.help( - "verify the lifetime relationships in the `trait` and `impl` between the \ - `self` argument, the other inputs and its output", - ); - } + err.note("the lifetime requirements from the `trait` could not be satisfied by the `impl`"); + err.help( + "verify the lifetime relationships in the `trait` and `impl` between the `self` \ + argument, the other inputs and its output", + ); err.emit(); } } diff --git a/src/test/ui/in-band-lifetimes/mismatched_trait_impl-2.stderr b/src/test/ui/in-band-lifetimes/mismatched_trait_impl-2.stderr index d8a2fa14333e..05413c3a2923 100644 --- a/src/test/ui/in-band-lifetimes/mismatched_trait_impl-2.stderr +++ b/src/test/ui/in-band-lifetimes/mismatched_trait_impl-2.stderr @@ -11,7 +11,7 @@ LL | fn deref(&self) -> &Self::Target; | = note: expected `fn(&Struct) -> &(dyn Trait + 'static)` found `fn(&Struct) -> &dyn Trait` - = note: the lifetime requirements from the `trait` could not be fulfilled by the `impl` + = note: the lifetime requirements from the `trait` could not be satisfied by the `impl` = help: verify the lifetime relationships in the `trait` and `impl` between the `self` argument, the other inputs and its output error: aborting due to previous error diff --git a/src/test/ui/in-band-lifetimes/mismatched_trait_impl.nll.stderr b/src/test/ui/in-band-lifetimes/mismatched_trait_impl.nll.stderr index 88edf10a66f0..c58f78b6719a 100644 --- a/src/test/ui/in-band-lifetimes/mismatched_trait_impl.nll.stderr +++ b/src/test/ui/in-band-lifetimes/mismatched_trait_impl.nll.stderr @@ -9,7 +9,7 @@ LL | fn foo(&self, x: &u32, y: &'a u32) -> &'a u32 { | = note: expected `fn(&i32, &'a u32, &u32) -> &'a u32` found `fn(&i32, &u32, &u32) -> &u32` - = note: the lifetime requirements from the `trait` could not be fulfilled by the `impl` + = note: the lifetime requirements from the `trait` could not be satisfied by the `impl` = help: verify the lifetime relationships in the `trait` and `impl` between the `self` argument, the other inputs and its output error: aborting due to previous error diff --git a/src/test/ui/in-band-lifetimes/mismatched_trait_impl.stderr b/src/test/ui/in-band-lifetimes/mismatched_trait_impl.stderr index dcdc6d330c0e..4dbd4ac6a852 100644 --- a/src/test/ui/in-band-lifetimes/mismatched_trait_impl.stderr +++ b/src/test/ui/in-band-lifetimes/mismatched_trait_impl.stderr @@ -9,7 +9,7 @@ LL | fn foo(&self, x: &u32, y: &'a u32) -> &'a u32 { | = note: expected `fn(&i32, &'a u32, &u32) -> &'a u32` found `fn(&i32, &u32, &u32) -> &u32` - = note: the lifetime requirements from the `trait` could not be fulfilled by the `impl` + = note: the lifetime requirements from the `trait` could not be satisfied by the `impl` = help: verify the lifetime relationships in the `trait` and `impl` between the `self` argument, the other inputs and its output error[E0623]: lifetime mismatch diff --git a/src/test/ui/lifetimes/lifetime-mismatch-between-trait-and-impl.stderr b/src/test/ui/lifetimes/lifetime-mismatch-between-trait-and-impl.stderr index fa2ea83deefb..0e56473c5b6a 100644 --- a/src/test/ui/lifetimes/lifetime-mismatch-between-trait-and-impl.stderr +++ b/src/test/ui/lifetimes/lifetime-mismatch-between-trait-and-impl.stderr @@ -9,7 +9,7 @@ LL | fn foo<'a>(x: &'a i32, y: &'a i32) -> &'a i32 { | = note: expected `fn(&i32, &'a i32) -> &'a i32` found `fn(&i32, &i32) -> &i32` - = note: the lifetime requirements from the `trait` could not be fulfilled by the `impl` + = note: the lifetime requirements from the `trait` could not be satisfied by the `impl` = help: verify the lifetime relationships in the `trait` and `impl` between the `self` argument, the other inputs and its output error: aborting due to previous error diff --git a/src/test/ui/traits/trait-param-without-lifetime-constraint.stderr b/src/test/ui/traits/trait-param-without-lifetime-constraint.stderr index 912d3a8d28de..724062b1a589 100644 --- a/src/test/ui/traits/trait-param-without-lifetime-constraint.stderr +++ b/src/test/ui/traits/trait-param-without-lifetime-constraint.stderr @@ -2,7 +2,7 @@ error: `impl` item signature doesn't match `trait` item signature --> $DIR/trait-param-without-lifetime-constraint.rs:14:5 | LL | pub trait HaveRelationship { - | -- in order for `impl` items to be able to implement the method, this type parameter might need a lifetime restriction like `To: 'a` + | -- for `impl` items to implement the method, this type parameter might need a lifetime restriction like `To: 'a` LL | fn get_relation(&self) -> To; | ----------------------------- expected fn(&Article) -> &ProofReader ... @@ -11,7 +11,7 @@ LL | fn get_relation(&self) -> &ProofReader { | = note: expected `fn(&Article) -> &ProofReader` found `fn(&Article) -> &ProofReader` - = note: the lifetime requirements from the `trait` could not be fulfilled by the `impl` + = note: the lifetime requirements from the `trait` could not be satisfied by the `impl` = help: verify the lifetime relationships in the `trait` and `impl` between the `self` argument, the other inputs and its output error: aborting due to previous error From 2e2f82053ffbf23997b736cde720ca4db4207504 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 27 Dec 2019 13:52:20 -0800 Subject: [PATCH 46/56] review comment: use FxIndexSet --- .../nice_region_error/trait_impl_difference.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs b/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs index 7ad7e8897df3..039671b98284 100644 --- a/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs +++ b/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs @@ -6,7 +6,7 @@ use crate::infer::error_reporting::nice_region_error::NiceRegionError; use crate::infer::lexical_region_resolve::RegionResolutionError; use crate::infer::{Subtype, ValuePairs}; use crate::traits::ObligationCauseCode::CompareImplMethodObligation; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::FxIndexSet; use rustc_errors::ErrorReported; use rustc_middle::ty::error::ExpectedFound; use rustc_middle::ty::fold::TypeFoldable; @@ -70,7 +70,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { // Because this is confusing as hell the first time you see it, we give a short message // explaining the situation and proposing constraining the type param with a named lifetime // so that the `impl` will have one to tie them together. - struct AssocTypeFinder(FxHashSet); + struct AssocTypeFinder(FxIndexSet); impl<'tcx> ty::fold::TypeVisitor<'tcx> for AssocTypeFinder { fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { debug!("assoc type finder ty {:?} {:?}", ty, ty.kind); @@ -80,7 +80,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { ty.super_visit_with(self) } } - let mut visitor = AssocTypeFinder(FxHashSet::default()); + let mut visitor = AssocTypeFinder(FxIndexSet::default()); trait_fn_sig.output().visit_with(&mut visitor); if let Some(id) = tcx.hir().as_local_hir_id(trait_def_id) { let parent_id = tcx.hir().get_parent_item(id); From d0d30b0a3ea6b715e275aa17ced2f7bc8d5207d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 7 Jan 2020 12:05:34 -0800 Subject: [PATCH 47/56] fix rebase --- .../nice_region_error/trait_impl_difference.rs | 6 +++--- src/test/ui/issues/issue-20831-debruijn.stderr | 4 ++-- .../regions/regions-normalize-in-where-clause-list.stderr | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs b/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs index 039671b98284..4019708df982 100644 --- a/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs +++ b/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs @@ -1,13 +1,13 @@ //! Error Reporting for `impl` items that do not match the obligations from their `trait`. -use crate::hir; -use crate::hir::def_id::DefId; use crate::infer::error_reporting::nice_region_error::NiceRegionError; use crate::infer::lexical_region_resolve::RegionResolutionError; use crate::infer::{Subtype, ValuePairs}; use crate::traits::ObligationCauseCode::CompareImplMethodObligation; use rustc_data_structures::fx::FxIndexSet; use rustc_errors::ErrorReported; +use rustc_hir::def_id::DefId; +use rustc_hir::ItemKind; use rustc_middle::ty::error::ExpectedFound; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::{self, Ty}; @@ -85,7 +85,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { if let Some(id) = tcx.hir().as_local_hir_id(trait_def_id) { let parent_id = tcx.hir().get_parent_item(id); let trait_item = tcx.hir().expect_item(parent_id); - if let hir::ItemKind::Trait(_, _, generics, _, _) = &trait_item.kind { + if let ItemKind::Trait(_, _, generics, _, _) = &trait_item.kind { for param_ty in visitor.0 { if let Some(generic) = generics.get_named(param_ty.name) { err.span_label(generic.span, &format!( diff --git a/src/test/ui/issues/issue-20831-debruijn.stderr b/src/test/ui/issues/issue-20831-debruijn.stderr index 160a4213a404..e7c1dcc5d698 100644 --- a/src/test/ui/issues/issue-20831-debruijn.stderr +++ b/src/test/ui/issues/issue-20831-debruijn.stderr @@ -87,8 +87,8 @@ note: ...so that the types are compatible | LL | fn subscribe(&mut self, t : Box::Output> + 'a>) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: expected `Publisher<'_>` - found `Publisher<'_>` + = note: expected `Publisher<'_>` + found `Publisher<'_>` error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements --> $DIR/issue-20831-debruijn.rs:28:33 diff --git a/src/test/ui/regions/regions-normalize-in-where-clause-list.stderr b/src/test/ui/regions/regions-normalize-in-where-clause-list.stderr index a274fd9d658a..dc93d620ca63 100644 --- a/src/test/ui/regions/regions-normalize-in-where-clause-list.stderr +++ b/src/test/ui/regions/regions-normalize-in-where-clause-list.stderr @@ -63,8 +63,8 @@ LL | | where <() as Project<'a, 'b>>::Item : Eq LL | | { LL | | } | |_^ - = note: expected `Project<'a, 'b>` - found `Project<'_, '_>` + = note: expected `Project<'a, 'b>` + found `Project<'_, '_>` error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements --> $DIR/regions-normalize-in-where-clause-list.rs:22:4 @@ -87,8 +87,8 @@ note: ...so that the types are compatible | LL | fn bar<'a, 'b>() | ^^^ - = note: expected `Project<'a, 'b>` - found `Project<'_, '_>` + = note: expected `Project<'a, 'b>` + found `Project<'_, '_>` error: aborting due to 3 previous errors From 2b35247d7a80e689dee6363730807687fdf42b5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 16 Feb 2020 17:59:29 -0800 Subject: [PATCH 48/56] Modify wording --- .../trait_impl_difference.rs | 83 ++++++++++++++++--- .../mismatched_trait_impl-2.stderr | 4 +- .../mismatched_trait_impl.stderr | 4 +- ...ime-mismatch-between-trait-and-impl.stderr | 4 +- ...t-param-without-lifetime-constraint.stderr | 9 +- 5 files changed, 84 insertions(+), 20 deletions(-) diff --git a/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs b/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs index 4019708df982..ca925233333c 100644 --- a/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs +++ b/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs @@ -2,15 +2,17 @@ use crate::infer::error_reporting::nice_region_error::NiceRegionError; use crate::infer::lexical_region_resolve::RegionResolutionError; -use crate::infer::{Subtype, ValuePairs}; +use crate::infer::{Subtype, TyCtxtInferExt, ValuePairs}; use crate::traits::ObligationCauseCode::CompareImplMethodObligation; use rustc_data_structures::fx::FxIndexSet; use rustc_errors::ErrorReported; +use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_hir::intravisit::Visitor; use rustc_hir::ItemKind; use rustc_middle::ty::error::ExpectedFound; use rustc_middle::ty::fold::TypeFoldable; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::Span; impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { @@ -59,8 +61,8 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { .tcx() .sess .struct_span_err(sp, "`impl` item signature doesn't match `trait` item signature"); - err.span_label(sp, &format!("found {:?}", found)); - err.span_label(trait_sp, &format!("expected {:?}", expected)); + err.span_label(sp, &format!("found `{:?}`", found)); + err.span_label(trait_sp, &format!("expected `{:?}`", expected)); let trait_fn_sig = tcx.fn_sig(trait_def_id); // Check the `trait`'s method's output to look for type parameters that might have @@ -73,7 +75,6 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { struct AssocTypeFinder(FxIndexSet); impl<'tcx> ty::fold::TypeVisitor<'tcx> for AssocTypeFinder { fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { - debug!("assoc type finder ty {:?} {:?}", ty, ty.kind); if let ty::Param(param) = ty.kind { self.0.insert(param); } @@ -86,18 +87,40 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { let parent_id = tcx.hir().get_parent_item(id); let trait_item = tcx.hir().expect_item(parent_id); if let ItemKind::Trait(_, _, generics, _, _) = &trait_item.kind { - for param_ty in visitor.0 { + for param_ty in &visitor.0 { if let Some(generic) = generics.get_named(param_ty.name) { - err.span_label(generic.span, &format!( - "for `impl` items to implement the method, this type parameter might \ - need a lifetime restriction like `{}: 'a`", - param_ty.name, - )); + err.span_label( + generic.span, + "this type parameter might not have a lifetime compatible with the \ + `impl`", + ); } } } } + // Get the span of all the used type parameters in the method. + let assoc_item = self.tcx().associated_item(trait_def_id); + let mut visitor = TypeParamSpanVisitor { tcx: self.tcx(), types: vec![] }; + match assoc_item.kind { + ty::AssocKind::Method => { + let hir = self.tcx().hir(); + if let Some(hir_id) = hir.as_local_hir_id(assoc_item.def_id) { + if let Some(decl) = hir.fn_decl_by_hir_id(hir_id) { + visitor.visit_fn_decl(decl); + } + } + } + _ => {} + } + for span in visitor.types { + err.span_label( + span, + "you might want to borrow this type parameter in the trait to make it match the \ + `impl`", + ); + } + if let Some((expected, found)) = tcx .infer_ctxt() .enter(|infcx| infcx.expected_found_str_ty(&ExpectedFound { expected, found })) @@ -116,3 +139,41 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { err.emit(); } } + +struct TypeParamSpanVisitor<'tcx> { + tcx: TyCtxt<'tcx>, + types: Vec, +} + +impl Visitor<'tcx> for TypeParamSpanVisitor<'tcx> { + type Map = hir::intravisit::Map<'tcx>; + + fn nested_visit_map<'this>( + &'this mut self, + ) -> hir::intravisit::NestedVisitorMap<'this, Self::Map> { + hir::intravisit::NestedVisitorMap::OnlyBodies(&self.tcx.hir()) + } + + fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) { + match arg.kind { + hir::TyKind::Slice(_) | hir::TyKind::Tup(_) | hir::TyKind::Array(..) => { + hir::intravisit::walk_ty(self, arg); + } + hir::TyKind::Path(hir::QPath::Resolved(None, path)) => match &path.segments { + [segment] + if segment + .res + .map(|res| match res { + hir::def::Res::Def(hir::def::DefKind::TyParam, _) => true, + _ => false, + }) + .unwrap_or(false) => + { + self.types.push(path.span); + } + _ => {} + }, + _ => {} + } + } +} diff --git a/src/test/ui/in-band-lifetimes/mismatched_trait_impl-2.stderr b/src/test/ui/in-band-lifetimes/mismatched_trait_impl-2.stderr index 05413c3a2923..61a8674a2310 100644 --- a/src/test/ui/in-band-lifetimes/mismatched_trait_impl-2.stderr +++ b/src/test/ui/in-band-lifetimes/mismatched_trait_impl-2.stderr @@ -2,12 +2,12 @@ error: `impl` item signature doesn't match `trait` item signature --> $DIR/mismatched_trait_impl-2.rs:8:5 | LL | fn deref(&self) -> &dyn Trait { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found fn(&Struct) -> &dyn Trait + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(&Struct) -> &dyn Trait` | ::: $SRC_DIR/libcore/ops/deref.rs:LL:COL | LL | fn deref(&self) -> &Self::Target; - | --------------------------------- expected fn(&Struct) -> &(dyn Trait + 'static) + | --------------------------------- expected `fn(&Struct) -> &(dyn Trait + 'static)` | = note: expected `fn(&Struct) -> &(dyn Trait + 'static)` found `fn(&Struct) -> &dyn Trait` diff --git a/src/test/ui/in-band-lifetimes/mismatched_trait_impl.stderr b/src/test/ui/in-band-lifetimes/mismatched_trait_impl.stderr index 4dbd4ac6a852..ca6102914553 100644 --- a/src/test/ui/in-band-lifetimes/mismatched_trait_impl.stderr +++ b/src/test/ui/in-band-lifetimes/mismatched_trait_impl.stderr @@ -2,10 +2,10 @@ error: `impl` item signature doesn't match `trait` item signature --> $DIR/mismatched_trait_impl.rs:9:5 | LL | fn foo(&self, x: &'a u32, y: &u32) -> &'a u32; - | ---------------------------------------------- expected fn(&i32, &'a u32, &u32) -> &'a u32 + | ---------------------------------------------- expected `fn(&i32, &'a u32, &u32) -> &'a u32` ... LL | fn foo(&self, x: &u32, y: &'a u32) -> &'a u32 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found fn(&i32, &u32, &u32) -> &u32 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(&i32, &u32, &u32) -> &u32` | = note: expected `fn(&i32, &'a u32, &u32) -> &'a u32` found `fn(&i32, &u32, &u32) -> &u32` diff --git a/src/test/ui/lifetimes/lifetime-mismatch-between-trait-and-impl.stderr b/src/test/ui/lifetimes/lifetime-mismatch-between-trait-and-impl.stderr index 0e56473c5b6a..53f6aae5ff6e 100644 --- a/src/test/ui/lifetimes/lifetime-mismatch-between-trait-and-impl.stderr +++ b/src/test/ui/lifetimes/lifetime-mismatch-between-trait-and-impl.stderr @@ -2,10 +2,10 @@ error: `impl` item signature doesn't match `trait` item signature --> $DIR/lifetime-mismatch-between-trait-and-impl.rs:6:5 | LL | fn foo<'a>(x: &i32, y: &'a i32) -> &'a i32; - | ------------------------------------------- expected fn(&i32, &'a i32) -> &'a i32 + | ------------------------------------------- expected `fn(&i32, &'a i32) -> &'a i32` ... LL | fn foo<'a>(x: &'a i32, y: &'a i32) -> &'a i32 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found fn(&i32, &i32) -> &i32 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(&i32, &i32) -> &i32` | = note: expected `fn(&i32, &'a i32) -> &'a i32` found `fn(&i32, &i32) -> &i32` diff --git a/src/test/ui/traits/trait-param-without-lifetime-constraint.stderr b/src/test/ui/traits/trait-param-without-lifetime-constraint.stderr index 724062b1a589..a7be2d3365c1 100644 --- a/src/test/ui/traits/trait-param-without-lifetime-constraint.stderr +++ b/src/test/ui/traits/trait-param-without-lifetime-constraint.stderr @@ -2,12 +2,15 @@ error: `impl` item signature doesn't match `trait` item signature --> $DIR/trait-param-without-lifetime-constraint.rs:14:5 | LL | pub trait HaveRelationship { - | -- for `impl` items to implement the method, this type parameter might need a lifetime restriction like `To: 'a` + | -- this type parameter might not have a lifetime compatible with the `impl` LL | fn get_relation(&self) -> To; - | ----------------------------- expected fn(&Article) -> &ProofReader + | ----------------------------- + | | | + | | you might want to borrow this type parameter in the trait to make it match the `impl` + | expected `fn(&Article) -> &ProofReader` ... LL | fn get_relation(&self) -> &ProofReader { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found fn(&Article) -> &ProofReader + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(&Article) -> &ProofReader` | = note: expected `fn(&Article) -> &ProofReader` found `fn(&Article) -> &ProofReader` From 500504c0bd79996784496d4ffa7a153ed5d10dda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 29 Mar 2020 19:53:58 -0700 Subject: [PATCH 49/56] fix rebase --- .../nice_region_error/trait_impl_difference.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs b/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs index ca925233333c..c0046758b07f 100644 --- a/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs +++ b/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs @@ -148,10 +148,8 @@ struct TypeParamSpanVisitor<'tcx> { impl Visitor<'tcx> for TypeParamSpanVisitor<'tcx> { type Map = hir::intravisit::Map<'tcx>; - fn nested_visit_map<'this>( - &'this mut self, - ) -> hir::intravisit::NestedVisitorMap<'this, Self::Map> { - hir::intravisit::NestedVisitorMap::OnlyBodies(&self.tcx.hir()) + fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap { + hir::intravisit::NestedVisitorMap::OnlyBodies(self.tcx.hir()) } fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) { From c52dbbc64354942c244c9f497e73d2e0814deaf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 13 Apr 2020 21:31:21 -0700 Subject: [PATCH 50/56] fix rebase --- .../error_reporting/nice_region_error/trait_impl_difference.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs b/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs index c0046758b07f..120594df6719 100644 --- a/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs +++ b/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs @@ -146,7 +146,7 @@ struct TypeParamSpanVisitor<'tcx> { } impl Visitor<'tcx> for TypeParamSpanVisitor<'tcx> { - type Map = hir::intravisit::Map<'tcx>; + type Map = rustc_middle::hir::map::Map<'tcx>; fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap { hir::intravisit::NestedVisitorMap::OnlyBodies(self.tcx.hir()) From 7d5415b5a2faf5b8f76156eedfd66f94b91660a0 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Tue, 26 May 2020 23:14:55 -0700 Subject: [PATCH 51/56] Add additional checks for isize overflow We now perform the correct checks even if the pointer size differs between the host and target. Signed-off-by: Joe Richey --- src/librustc_middle/mir/interpret/pointer.rs | 15 +++++++++++++-- src/librustc_mir/interpret/intrinsics.rs | 5 ++--- src/test/ui/consts/offset_ub.stderr | 4 ++-- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/librustc_middle/mir/interpret/pointer.rs b/src/librustc_middle/mir/interpret/pointer.rs index 019c96bc511e..ccad4f0a135a 100644 --- a/src/librustc_middle/mir/interpret/pointer.rs +++ b/src/librustc_middle/mir/interpret/pointer.rs @@ -24,6 +24,12 @@ pub trait PointerArithmetic: HasDataLayout { u64::try_from(max_usize_plus_1 - 1).unwrap() } + #[inline] + fn machine_isize_min(&self) -> i64 { + let max_isize_plus_1 = 1i128 << (self.pointer_size().bits() - 1); + i64::try_from(-max_isize_plus_1).unwrap() + } + #[inline] fn machine_isize_max(&self) -> i64 { let max_isize_plus_1 = 1u128 << (self.pointer_size().bits() - 1); @@ -42,18 +48,23 @@ pub trait PointerArithmetic: HasDataLayout { #[inline] fn overflowing_offset(&self, val: u64, i: u64) -> (u64, bool) { + // We do not need to check if i fits in a machine usize. If it doesn't, + // either the wrapping_add will wrap or res will not fit in a pointer. let res = val.overflowing_add(i); self.truncate_to_ptr(res) } #[inline] fn overflowing_signed_offset(&self, val: u64, i: i64) -> (u64, bool) { + // We need to make sure that i fits in a machine isize. let n = uabs(i); if i >= 0 { - self.overflowing_offset(val, n) + let (val, over) = self.overflowing_offset(val, n); + (val, over || i > self.machine_isize_max()) } else { let res = val.overflowing_sub(n); - self.truncate_to_ptr(res) + let (val, over) = self.truncate_to_ptr(res); + (val, over || i < self.machine_isize_min()) } } diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs index 239115076bc4..55f254f57326 100644 --- a/src/librustc_mir/interpret/intrinsics.rs +++ b/src/librustc_mir/interpret/intrinsics.rs @@ -441,9 +441,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // We cannot overflow i64 as a type's size must be <= isize::MAX. let pointee_size = i64::try_from(self.layout_of(pointee_ty)?.size.bytes()).unwrap(); // The computed offset, in bytes, cannot overflow an isize. - let offset_bytes = offset_count - .checked_mul(pointee_size) - .ok_or(err_ub_format!("inbounds pointer arithmetic: overflow computing offset"))?; + let offset_bytes = + offset_count.checked_mul(pointee_size).ok_or(err_ub!(PointerArithOverflow))?; // The offset being in bounds cannot rely on "wrapping around" the address space. // So, first rule out overflows in the pointer arithmetic. let offset_ptr = ptr.ptr_signed_offset(offset_bytes, self)?; diff --git a/src/test/ui/consts/offset_ub.stderr b/src/test/ui/consts/offset_ub.stderr index e808939682f3..0ab81cc0c5b3 100644 --- a/src/test/ui/consts/offset_ub.stderr +++ b/src/test/ui/consts/offset_ub.stderr @@ -51,7 +51,7 @@ error: any use of this value will cause an error LL | intrinsics::offset(self, count) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | inbounds pointer arithmetic: overflow computing offset + | overflowing in-bounds pointer arithmetic | inside `std::ptr::const_ptr::::offset` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL | inside `OVERFLOW` at $DIR/offset_ub.rs:11:43 | @@ -66,7 +66,7 @@ error: any use of this value will cause an error LL | intrinsics::offset(self, count) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | inbounds pointer arithmetic: overflow computing offset + | overflowing in-bounds pointer arithmetic | inside `std::ptr::const_ptr::::offset` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL | inside `UNDERFLOW` at $DIR/offset_ub.rs:12:44 | From cb6408afc64cd364ccf3b8144d49b32e8403883c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 27 May 2020 19:24:09 -0700 Subject: [PATCH 52/56] Fix rebase --- .../nice_region_error/trait_impl_difference.rs | 7 ++++--- src/test/ui/error-codes/E0490.stderr | 4 ++-- .../ui/in-band-lifetimes/mismatched_trait_impl.nll.stderr | 4 ++-- src/test/ui/issues/issue-20225.stderr | 4 ---- 4 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs b/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs index 120594df6719..8180dd5ef34f 100644 --- a/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs +++ b/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs @@ -83,7 +83,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { } let mut visitor = AssocTypeFinder(FxIndexSet::default()); trait_fn_sig.output().visit_with(&mut visitor); - if let Some(id) = tcx.hir().as_local_hir_id(trait_def_id) { + if let Some(id) = trait_def_id.as_local().map(|id| tcx.hir().as_local_hir_id(id)) { let parent_id = tcx.hir().get_parent_item(id); let trait_item = tcx.hir().expect_item(parent_id); if let ItemKind::Trait(_, _, generics, _, _) = &trait_item.kind { @@ -103,9 +103,10 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { let assoc_item = self.tcx().associated_item(trait_def_id); let mut visitor = TypeParamSpanVisitor { tcx: self.tcx(), types: vec![] }; match assoc_item.kind { - ty::AssocKind::Method => { + ty::AssocKind::Fn => { let hir = self.tcx().hir(); - if let Some(hir_id) = hir.as_local_hir_id(assoc_item.def_id) { + if let Some(hir_id) = assoc_item.def_id.as_local().map(|id| hir.as_local_hir_id(id)) + { if let Some(decl) = hir.fn_decl_by_hir_id(hir_id) { visitor.visit_fn_decl(decl); } diff --git a/src/test/ui/error-codes/E0490.stderr b/src/test/ui/error-codes/E0490.stderr index 03fce213605e..9ba5bc330ea9 100644 --- a/src/test/ui/error-codes/E0490.stderr +++ b/src/test/ui/error-codes/E0490.stderr @@ -58,8 +58,8 @@ note: ...so that the expression is assignable | LL | let x: &'a _ = &y; | ^^ - = note: expected `&'a &()` - found `&'a &'b ()` + = note: expected `&'a &()` + found `&'a &'b ()` note: but, the lifetime must be valid for the lifetime `'a` as defined on the function body at 1:6... --> $DIR/E0490.rs:1:6 | diff --git a/src/test/ui/in-band-lifetimes/mismatched_trait_impl.nll.stderr b/src/test/ui/in-band-lifetimes/mismatched_trait_impl.nll.stderr index c58f78b6719a..908a81bedb0c 100644 --- a/src/test/ui/in-band-lifetimes/mismatched_trait_impl.nll.stderr +++ b/src/test/ui/in-band-lifetimes/mismatched_trait_impl.nll.stderr @@ -2,10 +2,10 @@ error: `impl` item signature doesn't match `trait` item signature --> $DIR/mismatched_trait_impl.rs:9:5 | LL | fn foo(&self, x: &'a u32, y: &u32) -> &'a u32; - | ---------------------------------------------- expected fn(&i32, &'a u32, &u32) -> &'a u32 + | ---------------------------------------------- expected `fn(&i32, &'a u32, &u32) -> &'a u32` ... LL | fn foo(&self, x: &u32, y: &'a u32) -> &'a u32 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found fn(&i32, &u32, &u32) -> &u32 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(&i32, &u32, &u32) -> &u32` | = note: expected `fn(&i32, &'a u32, &u32) -> &'a u32` found `fn(&i32, &u32, &u32) -> &u32` diff --git a/src/test/ui/issues/issue-20225.stderr b/src/test/ui/issues/issue-20225.stderr index 133dbc554c08..3bcc50ded842 100644 --- a/src/test/ui/issues/issue-20225.stderr +++ b/src/test/ui/issues/issue-20225.stderr @@ -8,8 +8,6 @@ LL | extern "rust-call" fn call(&self, (_,): (T,)) {} | = note: expected fn pointer `extern "rust-call" fn(&Foo, (&'a T,))` found fn pointer `extern "rust-call" fn(&Foo, (T,))` - = help: type parameters must be constrained to match other types - = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters error[E0053]: method `call_mut` has an incompatible type for trait --> $DIR/issue-20225.rs:11:3 @@ -21,8 +19,6 @@ LL | extern "rust-call" fn call_mut(&mut self, (_,): (T,)) {} | = note: expected fn pointer `extern "rust-call" fn(&mut Foo, (&'a T,))` found fn pointer `extern "rust-call" fn(&mut Foo, (T,))` - = help: type parameters must be constrained to match other types - = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters error[E0053]: method `call_once` has an incompatible type for trait --> $DIR/issue-20225.rs:18:3 From f213acf4db81a33308ab2d53b5927108d63a2d7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 27 May 2020 20:58:05 -0700 Subject: [PATCH 53/56] review comments: change wording and visual output --- .../trait_impl_difference.rs | 63 +++++-------------- .../mismatched_trait_impl-2.stderr | 2 +- .../mismatched_trait_impl.nll.stderr | 2 +- .../mismatched_trait_impl.stderr | 2 +- ...ime-mismatch-between-trait-and-impl.stderr | 2 +- ...t-param-without-lifetime-constraint.stderr | 14 ++--- 6 files changed, 26 insertions(+), 59 deletions(-) diff --git a/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs b/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs index 8180dd5ef34f..1b6e5c2116b3 100644 --- a/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs +++ b/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs @@ -4,16 +4,13 @@ use crate::infer::error_reporting::nice_region_error::NiceRegionError; use crate::infer::lexical_region_resolve::RegionResolutionError; use crate::infer::{Subtype, TyCtxtInferExt, ValuePairs}; use crate::traits::ObligationCauseCode::CompareImplMethodObligation; -use rustc_data_structures::fx::FxIndexSet; use rustc_errors::ErrorReported; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; -use rustc_hir::ItemKind; use rustc_middle::ty::error::ExpectedFound; -use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_span::Span; +use rustc_span::{MultiSpan, Span}; impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { /// Print the error message for lifetime errors when the `impl` doesn't conform to the `trait`. @@ -63,41 +60,6 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { .struct_span_err(sp, "`impl` item signature doesn't match `trait` item signature"); err.span_label(sp, &format!("found `{:?}`", found)); err.span_label(trait_sp, &format!("expected `{:?}`", expected)); - let trait_fn_sig = tcx.fn_sig(trait_def_id); - - // Check the `trait`'s method's output to look for type parameters that might have - // unconstrained lifetimes. If the method returns a type parameter and the `impl` has a - // borrow as the type parameter being implemented, the lifetimes will not match because - // a new lifetime is being introduced in the `impl` that is not present in the `trait`. - // Because this is confusing as hell the first time you see it, we give a short message - // explaining the situation and proposing constraining the type param with a named lifetime - // so that the `impl` will have one to tie them together. - struct AssocTypeFinder(FxIndexSet); - impl<'tcx> ty::fold::TypeVisitor<'tcx> for AssocTypeFinder { - fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { - if let ty::Param(param) = ty.kind { - self.0.insert(param); - } - ty.super_visit_with(self) - } - } - let mut visitor = AssocTypeFinder(FxIndexSet::default()); - trait_fn_sig.output().visit_with(&mut visitor); - if let Some(id) = trait_def_id.as_local().map(|id| tcx.hir().as_local_hir_id(id)) { - let parent_id = tcx.hir().get_parent_item(id); - let trait_item = tcx.hir().expect_item(parent_id); - if let ItemKind::Trait(_, _, generics, _, _) = &trait_item.kind { - for param_ty in &visitor.0 { - if let Some(generic) = generics.get_named(param_ty.name) { - err.span_label( - generic.span, - "this type parameter might not have a lifetime compatible with the \ - `impl`", - ); - } - } - } - } // Get the span of all the used type parameters in the method. let assoc_item = self.tcx().associated_item(trait_def_id); @@ -114,11 +76,12 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { } _ => {} } - for span in visitor.types { - err.span_label( + let mut type_param_span: MultiSpan = + visitor.types.iter().cloned().collect::>().into(); + for &span in &visitor.types { + type_param_span.push_span_label( span, - "you might want to borrow this type parameter in the trait to make it match the \ - `impl`", + "consider borrowing this type parameter in the trait".to_string(), ); } @@ -132,11 +95,17 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { // This fallback shouldn't be necessary, but let's keep it in just in case. err.note(&format!("expected `{:?}`\n found `{:?}`", expected, found)); } - err.note("the lifetime requirements from the `trait` could not be satisfied by the `impl`"); - err.help( - "verify the lifetime relationships in the `trait` and `impl` between the `self` \ - argument, the other inputs and its output", + err.span_help( + type_param_span, + "the lifetime requirements from the `impl` do not correspond to the requirements in \ + the `trait`", ); + if visitor.types.is_empty() { + err.help( + "verify the lifetime relationships in the `trait` and `impl` between the `self` \ + argument, the other inputs and its output", + ); + } err.emit(); } } diff --git a/src/test/ui/in-band-lifetimes/mismatched_trait_impl-2.stderr b/src/test/ui/in-band-lifetimes/mismatched_trait_impl-2.stderr index 61a8674a2310..b93d98ca39f4 100644 --- a/src/test/ui/in-band-lifetimes/mismatched_trait_impl-2.stderr +++ b/src/test/ui/in-band-lifetimes/mismatched_trait_impl-2.stderr @@ -11,7 +11,7 @@ LL | fn deref(&self) -> &Self::Target; | = note: expected `fn(&Struct) -> &(dyn Trait + 'static)` found `fn(&Struct) -> &dyn Trait` - = note: the lifetime requirements from the `trait` could not be satisfied by the `impl` + = help: the lifetime requirements from the `impl` do not correspond to the requirements in the `trait` = help: verify the lifetime relationships in the `trait` and `impl` between the `self` argument, the other inputs and its output error: aborting due to previous error diff --git a/src/test/ui/in-band-lifetimes/mismatched_trait_impl.nll.stderr b/src/test/ui/in-band-lifetimes/mismatched_trait_impl.nll.stderr index 908a81bedb0c..149c2aeb958c 100644 --- a/src/test/ui/in-band-lifetimes/mismatched_trait_impl.nll.stderr +++ b/src/test/ui/in-band-lifetimes/mismatched_trait_impl.nll.stderr @@ -9,7 +9,7 @@ LL | fn foo(&self, x: &u32, y: &'a u32) -> &'a u32 { | = note: expected `fn(&i32, &'a u32, &u32) -> &'a u32` found `fn(&i32, &u32, &u32) -> &u32` - = note: the lifetime requirements from the `trait` could not be satisfied by the `impl` + = help: the lifetime requirements from the `impl` do not correspond to the requirements in the `trait` = help: verify the lifetime relationships in the `trait` and `impl` between the `self` argument, the other inputs and its output error: aborting due to previous error diff --git a/src/test/ui/in-band-lifetimes/mismatched_trait_impl.stderr b/src/test/ui/in-band-lifetimes/mismatched_trait_impl.stderr index ca6102914553..9a0bd827850c 100644 --- a/src/test/ui/in-band-lifetimes/mismatched_trait_impl.stderr +++ b/src/test/ui/in-band-lifetimes/mismatched_trait_impl.stderr @@ -9,7 +9,7 @@ LL | fn foo(&self, x: &u32, y: &'a u32) -> &'a u32 { | = note: expected `fn(&i32, &'a u32, &u32) -> &'a u32` found `fn(&i32, &u32, &u32) -> &u32` - = note: the lifetime requirements from the `trait` could not be satisfied by the `impl` + = help: the lifetime requirements from the `impl` do not correspond to the requirements in the `trait` = help: verify the lifetime relationships in the `trait` and `impl` between the `self` argument, the other inputs and its output error[E0623]: lifetime mismatch diff --git a/src/test/ui/lifetimes/lifetime-mismatch-between-trait-and-impl.stderr b/src/test/ui/lifetimes/lifetime-mismatch-between-trait-and-impl.stderr index 53f6aae5ff6e..060e6954403c 100644 --- a/src/test/ui/lifetimes/lifetime-mismatch-between-trait-and-impl.stderr +++ b/src/test/ui/lifetimes/lifetime-mismatch-between-trait-and-impl.stderr @@ -9,7 +9,7 @@ LL | fn foo<'a>(x: &'a i32, y: &'a i32) -> &'a i32 { | = note: expected `fn(&i32, &'a i32) -> &'a i32` found `fn(&i32, &i32) -> &i32` - = note: the lifetime requirements from the `trait` could not be satisfied by the `impl` + = help: the lifetime requirements from the `impl` do not correspond to the requirements in the `trait` = help: verify the lifetime relationships in the `trait` and `impl` between the `self` argument, the other inputs and its output error: aborting due to previous error diff --git a/src/test/ui/traits/trait-param-without-lifetime-constraint.stderr b/src/test/ui/traits/trait-param-without-lifetime-constraint.stderr index a7be2d3365c1..4942dbe480ba 100644 --- a/src/test/ui/traits/trait-param-without-lifetime-constraint.stderr +++ b/src/test/ui/traits/trait-param-without-lifetime-constraint.stderr @@ -1,21 +1,19 @@ error: `impl` item signature doesn't match `trait` item signature --> $DIR/trait-param-without-lifetime-constraint.rs:14:5 | -LL | pub trait HaveRelationship { - | -- this type parameter might not have a lifetime compatible with the `impl` LL | fn get_relation(&self) -> To; - | ----------------------------- - | | | - | | you might want to borrow this type parameter in the trait to make it match the `impl` - | expected `fn(&Article) -> &ProofReader` + | ----------------------------- expected `fn(&Article) -> &ProofReader` ... LL | fn get_relation(&self) -> &ProofReader { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(&Article) -> &ProofReader` | = note: expected `fn(&Article) -> &ProofReader` found `fn(&Article) -> &ProofReader` - = note: the lifetime requirements from the `trait` could not be satisfied by the `impl` - = help: verify the lifetime relationships in the `trait` and `impl` between the `self` argument, the other inputs and its output +help: the lifetime requirements from the `impl` do not correspond to the requirements in the `trait` + --> $DIR/trait-param-without-lifetime-constraint.rs:10:31 + | +LL | fn get_relation(&self) -> To; + | ^^ consider borrowing this type parameter in the trait error: aborting due to previous error From cd6a8cae2aa07bd456a1816196e3c9aa2fcb72d6 Mon Sep 17 00:00:00 2001 From: CAD97 Date: Thu, 28 May 2020 01:11:46 -0400 Subject: [PATCH 54/56] FIx off-by-one in char::steps_between --- src/libcore/iter/range.rs | 2 +- src/libcore/tests/iter.rs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libcore/iter/range.rs b/src/libcore/iter/range.rs index 5db790e491c2..fc4454ede358 100644 --- a/src/libcore/iter/range.rs +++ b/src/libcore/iter/range.rs @@ -408,7 +408,7 @@ unsafe impl Step for char { let start = start as u32; let end = end as u32; if start <= end { - let count = end - start + 1; + let count = end - start; if start < 0xD800 && 0xE000 <= end { usize::try_from(count - 0x800).ok() } else { diff --git a/src/libcore/tests/iter.rs b/src/libcore/tests/iter.rs index 6605249f1800..c5d636ac8da5 100644 --- a/src/libcore/tests/iter.rs +++ b/src/libcore/tests/iter.rs @@ -1939,7 +1939,9 @@ fn test_char_range() { assert!(('\0'..=char::MAX).rev().eq((0..=char::MAX as u32).filter_map(char::from_u32).rev())); assert_eq!(('\u{D7FF}'..='\u{E000}').count(), 2); + assert_eq!(('\u{D7FF}'..='\u{E000}').size_hint(), (2, Some(2))); assert_eq!(('\u{D7FF}'..'\u{E000}').count(), 1); + assert_eq!(('\u{D7FF}'..'\u{E000}').size_hint(), (1, Some(1))); } #[test] From 0e3b31c641217542f02b40527b0d299d9534b920 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 28 May 2020 13:23:33 -0400 Subject: [PATCH 55/56] Update src/librustdoc/core.rs --- src/librustdoc/core.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 331ce1453e13..99ca7084c30d 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -228,7 +228,7 @@ where // Whitelist feature-gated lints to avoid feature errors when trying to // allow all lints. - // FIXME(LeSeulArtichaut): handle feature-gated lints properly. + // FIXME(#72694): handle feature-gated lints properly. let unsafe_op_in_unsafe_fn_name = rustc_lint::builtin::UNSAFE_OP_IN_UNSAFE_FN.name; whitelisted_lints.push(warnings_lint_name.to_owned()); From 1bd69702de07858ad9c7ae7ed89c3c99aea64550 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 28 May 2020 10:35:48 -0700 Subject: [PATCH 56/56] Account for `Self` as a type param --- .../trait_impl_difference.rs | 10 ++-- .../self-without-lifetime-constraint.rs | 53 +++++++++++++++++++ .../self-without-lifetime-constraint.stderr | 19 +++++++ 3 files changed, 79 insertions(+), 3 deletions(-) create mode 100644 src/test/ui/traits/self-without-lifetime-constraint.rs create mode 100644 src/test/ui/traits/self-without-lifetime-constraint.stderr diff --git a/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs b/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs index 1b6e5c2116b3..5f14f799fc7a 100644 --- a/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs +++ b/src/librustc_infer/infer/error_reporting/nice_region_error/trait_impl_difference.rs @@ -6,6 +6,7 @@ use crate::infer::{Subtype, TyCtxtInferExt, ValuePairs}; use crate::traits::ObligationCauseCode::CompareImplMethodObligation; use rustc_errors::ErrorReported; use rustc_hir as hir; +use rustc_hir::def::Res; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; use rustc_middle::ty::error::ExpectedFound; @@ -124,15 +125,17 @@ impl Visitor<'tcx> for TypeParamSpanVisitor<'tcx> { fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) { match arg.kind { - hir::TyKind::Slice(_) | hir::TyKind::Tup(_) | hir::TyKind::Array(..) => { - hir::intravisit::walk_ty(self, arg); + hir::TyKind::Rptr(_, ref mut_ty) => { + // We don't want to suggest looking into borrowing `&T` or `&Self`. + hir::intravisit::walk_ty(self, mut_ty.ty); + return; } hir::TyKind::Path(hir::QPath::Resolved(None, path)) => match &path.segments { [segment] if segment .res .map(|res| match res { - hir::def::Res::Def(hir::def::DefKind::TyParam, _) => true, + Res::SelfTy(_, _) | Res::Def(hir::def::DefKind::TyParam, _) => true, _ => false, }) .unwrap_or(false) => @@ -143,5 +146,6 @@ impl Visitor<'tcx> for TypeParamSpanVisitor<'tcx> { }, _ => {} } + hir::intravisit::walk_ty(self, arg); } } diff --git a/src/test/ui/traits/self-without-lifetime-constraint.rs b/src/test/ui/traits/self-without-lifetime-constraint.rs new file mode 100644 index 000000000000..99013d32ab8d --- /dev/null +++ b/src/test/ui/traits/self-without-lifetime-constraint.rs @@ -0,0 +1,53 @@ +use std::error::Error; +use std::fmt; + +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum ValueRef<'a> { + Null, + Integer(i64), + Real(f64), + Text(&'a [u8]), + Blob(&'a [u8]), +} + +impl<'a> ValueRef<'a> { + pub fn as_str(&self) -> FromSqlResult<&'a str, &'a &'a str> { + match *self { + ValueRef::Text(t) => { + std::str::from_utf8(t).map_err(|_| FromSqlError::InvalidType).map(|x| (x, &x)) + } + _ => Err(FromSqlError::InvalidType), + } + } +} + +#[derive(Debug)] +#[non_exhaustive] +pub enum FromSqlError { + InvalidType +} + +impl fmt::Display for FromSqlError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "InvalidType") + } +} + +impl Error for FromSqlError {} + +pub type FromSqlResult = Result<(T, K), FromSqlError>; + +pub trait FromSql: Sized { + fn column_result(value: ValueRef<'_>) -> FromSqlResult; +} + +impl FromSql for &str { + fn column_result(value: ValueRef<'_>) -> FromSqlResult<&str, &&str> { + //~^ ERROR `impl` item signature doesn't match `trait` item signature + value.as_str() + } +} + +pub fn main() { + println!("{}", "Hello World"); +} diff --git a/src/test/ui/traits/self-without-lifetime-constraint.stderr b/src/test/ui/traits/self-without-lifetime-constraint.stderr new file mode 100644 index 000000000000..6c7abe753e2b --- /dev/null +++ b/src/test/ui/traits/self-without-lifetime-constraint.stderr @@ -0,0 +1,19 @@ +error: `impl` item signature doesn't match `trait` item signature + --> $DIR/self-without-lifetime-constraint.rs:45:5 + | +LL | fn column_result(value: ValueRef<'_>) -> FromSqlResult; + | -------------------------------------------------------------------- expected `fn(ValueRef<'_>) -> std::result::Result<(&str, &&str), FromSqlError>` +... +LL | fn column_result(value: ValueRef<'_>) -> FromSqlResult<&str, &&str> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(ValueRef<'_>) -> std::result::Result<(&str, &&str), FromSqlError>` + | + = note: expected `fn(ValueRef<'_>) -> std::result::Result<(&str, &&str), _>` + found `fn(ValueRef<'_>) -> std::result::Result<(&str, &&str), _>` +help: the lifetime requirements from the `impl` do not correspond to the requirements in the `trait` + --> $DIR/self-without-lifetime-constraint.rs:41:60 + | +LL | fn column_result(value: ValueRef<'_>) -> FromSqlResult; + | ^^^^ consider borrowing this type parameter in the trait + +error: aborting due to previous error +