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 5f1c0911da2..d0050f801fc 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 67254811ec5..b49e4187fb8 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 402eac47c46..e04ed8b83de 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 98c0542f9c0..f24d6687399 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 37e2e047504..aee825a8113 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 d3ab7df817b..baba3cb1bab 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 29636a06709..eb453c4bb7b 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 8ef6090c743..8cc6f04c065 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 0d0765e971d..b4a083636b6 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 4b195f3a06e..48cf21d489a 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 ce1c9ae551e..5c01603ab88 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 d87b84f880a..288a4a41e07 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 41c2f1d93d2..d92f5cdeab5 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 288a4a41e07..7addfd15f1c 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 ee5bcf4b9f5..80d1f700684 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 edc28033c9b..d50230a8859 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(); - 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; + // 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, + } - 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) + 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(()) } - 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(); - } - } - } - - 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 d74df82bddd..6837aab7af1 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 6837aab7af1..3fa4cf2ca0d 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 3fa4cf2ca0d..5db790e491c 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 52cf068f0a5..6605249f180 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 8986ab322c0..fe4cd16c1f5 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 774146a679a..ffca742e9ab 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 00000000000..b574e921718 --- /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 00000000000..cfaab235124 --- /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 ffca742e9ab..99061d1fdeb 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 | 81 +++++++++++++++++++--------- 4 files changed, 83 insertions(+), 79 deletions(-) diff --git a/src/librustc_codegen_llvm/asm.rs b/src/librustc_codegen_llvm/asm.rs index fe4cd16c1f5..f3b46dd322a 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 99061d1fdeb..1bbaa4c4c00 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 b574e921718..cba069cd054 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 cfaab235124..5faf4082f32 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: reg_i32 +// CHECK-LABEL: reg32_i8 // CHECK: #APP // CHECK: mov.i32 {{[a-z0-9]+}}, {{[a-z0-9]+}}; // CHECK: #NO_APP -check!(reg_i32 i32 reg32 "mov.i32"); +check!(reg32_i8 i8 reg32 "mov.i32"); -// CHECK-LABEL: reg_f32 +// CHECK-LABEL: reg32_i16 // CHECK: #APP -// CHECK: mov.f32 {{[a-z0-9]+}}, {{[a-z0-9]+}}; +// CHECK: mov.i32 {{[a-z0-9]+}}, {{[a-z0-9]+}}; // CHECK: #NO_APP -check!(reg_f32 f32 freg32 "mov.f32"); +check!(reg32_i16 i16 reg32 "mov.i32"); -// CHECK-LABEL: reg_i54 +// CHECK-LABEL: reg32_i32 +// CHECK: #APP +// CHECK: mov.i32 {{[a-z0-9]+}}, {{[a-z0-9]+}}; +// CHECK: #NO_APP +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!(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_i64 i64 reg64 "mov.i64"); +check!(reg64_i8 i8 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-LABEL: reg64_i16 // CHECK: #APP // CHECK: mov.i64 {{[a-z0-9]+}}, {{[a-z0-9]+}}; // CHECK: #NO_APP -check!(reg_ptr ptr reg64 "mov.i64"); +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!(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!(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!(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 1bbaa4c4c00..a18a4dbd3e2 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 cba069cd054..43d16ae0f5d 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 5faf4082f32..b36efe16c64 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 0b68991fce2..c34a2931bc0 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 c34a2931bc0..50121b2c0fc 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 50121b2c0fc..ea560a6d709 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 b36efe16c64..4ee79d1bcc8 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 fc4be82ad90..0db9c702658 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 6a6098710e8..9b055beb99d 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 9006e4cfaf7..2d97fecf8a7 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 ca13433caec..7d21f9a9a66 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 85ba5fc0638..835183d171a 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 0781d7e6cac..40b5e4e2234 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 00000000000..f64242d568e --- /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 00000000000..ac120789694 --- /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 00000000000..b2b77586a50 --- /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 81985f9f625..65fc49c0b27 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 5bd534a16b8..805ba9c6b03 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 0db9c702658..b4748059262 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 d9e52af8900..061bc9750e1 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 70cc546199b..019c96bc511 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 b4748059262..239115076bc 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 ac120789694..4f943ed9ad1 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 b2b77586a50..e808939682f 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 90b2380d864..fd35cb6c3f7 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 c279213e5bd..243f81459e7 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 63b8d8c8da7..f19270e5561 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 a335fa2de41..bcadbd09107 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,6 +351,9 @@ 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) @@ -358,7 +361,25 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> { } false } - // `unsafe` function bodies allow unsafe without additional unsafe blocks + // 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 (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 fa783ddcf40..b052c839152 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 e55ddc26a94..7112ac35b08 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 6a6098710e8..87952d409c8 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 00000000000..80005fd4ef6 --- /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 00000000000..64874e590fb --- /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 00000000000..0ffb2827bfd --- /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 00000000000..be6af2a11c4 --- /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 | 38 +++++++---- .../unsafe/rfc-2585-unsafe_op_in_unsafe_fn.rs | 8 ++- .../rfc-2585-unsafe_op_in_unsafe_fn.stderr | 17 ++++- 8 files changed, 126 insertions(+), 51 deletions(-) diff --git a/src/librustc_lint/levels.rs b/src/librustc_lint/levels.rs index 274e57ae64c..70694720154 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 f19270e5561..c63d7215867 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 bcadbd09107..0434f3447bc 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 b052c839152..4e4f0dc74cb 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 80005fd4ef6..bf33ac67fca 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 64874e590fb..c5cad4a98d9 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 - | -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[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)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 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 0ffb2827bfd..7feb68b9857 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 be6af2a11c4..e812210498c 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 7feb68b9857..618c9115815 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 0434f3447bc..3cff214f441 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 c63d7215867..99bfb74c243 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 3cff214f441..177efa2fc0d 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 177efa2fc0d..7be640ae243 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 | 73 +++++++++-- .../rfc-2585-unsafe_op_in_unsafe_fn.stderr | 118 ++++++++++++++++-- 2 files changed, 169 insertions(+), 22 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 618c9115815..6dfb97c230d 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 } -fn main() { - unsf() - //~^ ERROR call to unsafe function is unsafe and requires unsafe function or 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 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 e812210498c..efc53f0b28b 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 00000000000..540612a7dce --- /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 00000000000..fda15159643 --- /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 6dfb97c230d..1e57b03ced4 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 efc53f0b28b..cc595df12cc 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 bf33ac67fca..61e512a12a1 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 70694720154..2e3fc162b53 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 7be640ae243..1f01bc0e195 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 2e3fc162b53..8dd683ae799 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 8dd683ae799..3d2ddf12a0a 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 bf59b3f2573..331ce1453e1 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 1cc5daafed1..cff83c3d5cd 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 3e39c8a7924..137cb83ccd3 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 3300293bb36..268008c2111 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 99700f2084e..4f658330758 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 adfc3f50858..31788cfa61c 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 a785a956ca9..160a4213a40 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 a8960f77563..7463af9332a 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 b8cafdc5c14..6bfb7af5444 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 69a6ab004fd..ba8d91b8455 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 58f206742f4..d003acd879a 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 5191deca281..3317aae83bb 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 37be450fd0a..8421dc1d0c1 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 4ee32847c5e..ba0a1748c5e 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 1952ee8269d..79ded5fc875 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 e8896516470..069b897603c 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 865e967fba3..c134b3b3ed5 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 6a34871c07e..ac8c55ccc8f 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 28873ab807f..147f7f35418 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 449a5b5fdd4..6e7d6152cd0 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 b2a7afaf1b4..2070ce257b1 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 58f74e4ee14..b24db1df18b 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 8fce1609d78..9e405d83140 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 c35516d2c08..a274fd9d658 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 2895a0ccdee..2c4769d8e37 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 b74f10f5075..da560107cea 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 58b79d21270..7478b53bd3c 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 c09418de518..f819faa2789 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 9fdcd4de495..46aa7db967a 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 e6029e0d462..e3c9d50dfe5 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 cc479aa17ce..66e7c87f2c3 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 695f3e47fb5..2e54a4e28e9 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 43f0b55856d..3ccfe1cede7 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 2601ca5e91e..f5b13f6c590 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 f1f8a663f21..9eed16fa994 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 328e98657ef..4d09fe74e16 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 638a0093fb2..d5725efbac3 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 748bc639a03..3cc9c1a7fb0 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 c1c4ec9ed7b..349f813b20d 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 15891c9e7a6..72960a41f93 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 7631831a81a..3fa94fac237 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 a8473c8a524..238f41782ba 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 4f658330758..fa782d04331 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 31788cfa61c..7c382b95ae9 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 3bcc50ded84..351973d444b 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 ace3e014647..cc95b31acb7 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 d07f305954b..57d145dcc20 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 fef83e6bbe2..4926068e583 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 b20fddb05ac..debb371db51 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 3317aae83bb..d029cd23a8b 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 27704b3e0a8..074f78027c3 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 069b897603c..cbe8d01f977 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 a8a7e97e6ac..b26891a6789 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 9e405d83140..40e9146d689 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 2c4769d8e37..b489290d9f7 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 da560107cea..6b2a041b95b 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 60ac7c09f04..6b3c40658a7 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 7478b53bd3c..271849fdef6 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 c14dfa3601a..0d19ea1ff25 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 d1960a8aab3..8ea45e4191d 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 52e49186241..517da553dc0 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 24c71c63103..7beffa0e5e4 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 03a251be1a9..691c751a080 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 1c3d057cbc9..f163790eb45 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 4c367fb9e9c..cb901cdeedf 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 66e7c87f2c3..ae901982817 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 2e54a4e28e9..5aa55d253aa 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 3ccfe1cede7..43f0b55856d 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 f5b13f6c590..2601ca5e91e 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 9eed16fa994..f1f8a663f21 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 4d09fe74e16..328e98657ef 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 d5725efbac3..638a0093fb2 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 3cc9c1a7fb0..748bc639a03 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 349f813b20d..d8a2fa14333 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 c245d78ae82..88edf10a66f 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 72960a41f93..dcdc6d330c0 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 3fa94fac237..7631831a81a 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 238f41782ba..a8473c8a524 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 fa782d04331..4f658330758 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 7c382b95ae9..31788cfa61c 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 351973d444b..133dbc554c0 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 cc95b31acb7..ace3e014647 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 57d145dcc20..fa2ea83deef 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 4926068e583..fef83e6bbe2 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 debb371db51..b20fddb05ac 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 d029cd23a8b..3317aae83bb 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 074f78027c3..27704b3e0a8 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 cbe8d01f977..069b897603c 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 b26891a6789..a8a7e97e6ac 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 40e9146d689..9e405d83140 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 b489290d9f7..2c4769d8e37 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 6b2a041b95b..da560107cea 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 6b3c40658a7..60ac7c09f04 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 271849fdef6..7478b53bd3c 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 0d19ea1ff25..c14dfa3601a 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 8ea45e4191d..d1960a8aab3 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 517da553dc0..52e49186241 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 00000000000..a79b74dcdde --- /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 00000000000..912d3a8d28d --- /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 7beffa0e5e4..24c71c63103 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 691c751a080..03a251be1a9 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 f163790eb45..1c3d057cbc9 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 cb901cdeedf..4c367fb9e9c 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 5aa55d253aa..7ad7e8897df 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 d8a2fa14333..05413c3a292 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 88edf10a66f..c58f78b6719 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 dcdc6d330c0..4dbd4ac6a85 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 fa2ea83deef..0e56473c5b6 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 912d3a8d28d..724062b1a58 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 7ad7e8897df..039671b9828 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 039671b9828..4019708df98 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 160a4213a40..e7c1dcc5d69 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 a274fd9d658..dc93d620ca6 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 4019708df98..ca925233333 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 05413c3a292..61a8674a231 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 4dbd4ac6a85..ca610291455 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 0e56473c5b6..53f6aae5ff6 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 724062b1a58..a7be2d3365c 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 ca925233333..c0046758b07 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 c0046758b07..120594df671 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 019c96bc511..ccad4f0a135 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 239115076bc..55f254f5732 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 e808939682f..0ab81cc0c5b 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 120594df671..8180dd5ef34 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 03fce213605..9ba5bc330ea 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 c58f78b6719..908a81bedb0 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 133dbc554c0..3bcc50ded84 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 8180dd5ef34..1b6e5c2116b 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 61a8674a231..b93d98ca39f 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 908a81bedb0..149c2aeb958 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 ca610291455..9a0bd827850 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 53f6aae5ff6..060e6954403 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 a7be2d3365c..4942dbe480b 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 5db790e491c..fc4454ede35 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 6605249f180..c5d636ac8da 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 331ce1453e1..99ca7084c30 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 1b6e5c2116b..5f14f799fc7 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 00000000000..99013d32ab8 --- /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 00000000000..6c7abe753e2 --- /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 +