diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index bb09f701531cf..6d6438920c09b 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -754,7 +754,7 @@ fn validate_generic_param_order( GenericParamKind::Type { default: _ } => (ParamKindOrd::Type, ident), GenericParamKind::Const { ref ty, kw_span: _, default: _ } => { let ty = pprust::ty_to_string(ty); - let unordered = sess.features_untracked().const_generics; + let unordered = sess.features_untracked().unordered_const_ty_params(); (ParamKindOrd::Const { unordered }, Some(format!("const {}: {}", param.ident, ty))) } }; @@ -1213,8 +1213,41 @@ impl<'a> Visitor<'a> for AstValidator<'a> { deny_equality_constraints(self, predicate, generics); } } - - visit::walk_generics(self, generics) + walk_list!(self, visit_generic_param, &generics.params); + for predicate in &generics.where_clause.predicates { + match predicate { + WherePredicate::BoundPredicate(bound_pred) => { + // A type binding, eg `for<'c> Foo: Send+Clone+'c` + self.check_late_bound_lifetime_defs(&bound_pred.bound_generic_params); + + // This is slightly complicated. Our representation for poly-trait-refs contains a single + // binder and thus we only allow a single level of quantification. However, + // the syntax of Rust permits quantification in two places in where clauses, + // e.g., `T: for <'a> Foo<'a>` and `for <'a, 'b> &'b T: Foo<'a>`. If both are + // defined, then error. + if !bound_pred.bound_generic_params.is_empty() { + for bound in &bound_pred.bounds { + match bound { + GenericBound::Trait(t, _) => { + if !t.bound_generic_params.is_empty() { + struct_span_err!( + self.err_handler(), + t.span, + E0316, + "nested quantification of lifetimes" + ) + .emit(); + } + } + GenericBound::Outlives(_) => {} + } + } + } + } + _ => {} + } + self.visit_where_predicate(predicate); + } } fn visit_generic_param(&mut self, param: &'a GenericParam) { @@ -1263,14 +1296,6 @@ impl<'a> Visitor<'a> for AstValidator<'a> { visit::walk_pat(self, pat) } - fn visit_where_predicate(&mut self, p: &'a WherePredicate) { - if let &WherePredicate::BoundPredicate(ref bound_predicate) = p { - // A type binding, eg `for<'c> Foo: Send+Clone+'c` - self.check_late_bound_lifetime_defs(&bound_predicate.bound_generic_params); - } - visit::walk_where_predicate(self, p); - } - fn visit_poly_trait_ref(&mut self, t: &'a PolyTraitRef, m: &'a TraitBoundModifier) { self.check_late_bound_lifetime_defs(&t.bound_generic_params); visit::walk_poly_trait_ref(self, t, m); diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index f823792fabe63..b67689ce523f2 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -2,6 +2,7 @@ use crate::common::Funclet; use crate::context::CodegenCx; use crate::llvm::{self, BasicBlock, False}; use crate::llvm::{AtomicOrdering, AtomicRmwBinOp, SynchronizationScope}; +use crate::llvm_util; use crate::type_::Type; use crate::type_of::LayoutLlvmExt; use crate::value::Value; @@ -16,7 +17,7 @@ use rustc_data_structures::small_c_str::SmallCStr; use rustc_hir::def_id::DefId; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_span::{sym, Span}; +use rustc_span::Span; use rustc_target::abi::{self, Align, Size}; use rustc_target::spec::{HasTargetSpec, Target}; use std::borrow::Cow; @@ -669,81 +670,47 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } fn fptoui_sat(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> Option<&'ll Value> { - // WebAssembly has saturating floating point to integer casts if the - // `nontrapping-fptoint` target feature is activated. We'll use those if - // they are available. - if self.sess().target.arch == "wasm32" - && self.sess().target_features.contains(&sym::nontrapping_dash_fptoint) - { + if llvm_util::get_version() >= (12, 0, 0) { let src_ty = self.cx.val_ty(val); let float_width = self.cx.float_width(src_ty); let int_width = self.cx.int_width(dest_ty); - let name = match (int_width, float_width) { - (32, 32) => Some("llvm.wasm.trunc.saturate.unsigned.i32.f32"), - (32, 64) => Some("llvm.wasm.trunc.saturate.unsigned.i32.f64"), - (64, 32) => Some("llvm.wasm.trunc.saturate.unsigned.i64.f32"), - (64, 64) => Some("llvm.wasm.trunc.saturate.unsigned.i64.f64"), - _ => None, - }; - if let Some(name) = name { - let intrinsic = self.get_intrinsic(name); - return Some(self.call(intrinsic, &[val], None)); - } + let name = format!("llvm.fptoui.sat.i{}.f{}", int_width, float_width); + let intrinsic = self.get_intrinsic(&name); + return Some(self.call(intrinsic, &[val], None)); } + None } fn fptosi_sat(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> Option<&'ll Value> { - // WebAssembly has saturating floating point to integer casts if the - // `nontrapping-fptoint` target feature is activated. We'll use those if - // they are available. - if self.sess().target.arch == "wasm32" - && self.sess().target_features.contains(&sym::nontrapping_dash_fptoint) - { + if llvm_util::get_version() >= (12, 0, 0) { let src_ty = self.cx.val_ty(val); let float_width = self.cx.float_width(src_ty); let int_width = self.cx.int_width(dest_ty); - let name = match (int_width, float_width) { - (32, 32) => Some("llvm.wasm.trunc.saturate.signed.i32.f32"), - (32, 64) => Some("llvm.wasm.trunc.saturate.signed.i32.f64"), - (64, 32) => Some("llvm.wasm.trunc.saturate.signed.i64.f32"), - (64, 64) => Some("llvm.wasm.trunc.saturate.signed.i64.f64"), - _ => None, - }; - if let Some(name) = name { - let intrinsic = self.get_intrinsic(name); - return Some(self.call(intrinsic, &[val], None)); - } + let name = format!("llvm.fptosi.sat.i{}.f{}", int_width, float_width); + let intrinsic = self.get_intrinsic(&name); + return Some(self.call(intrinsic, &[val], None)); } - None - } - fn fptosui_may_trap(&self, val: &'ll Value, dest_ty: &'ll Type) -> bool { - // Most of the time we'll be generating the `fptosi` or `fptoui` - // instruction for floating-point-to-integer conversions. These - // instructions by definition in LLVM do not trap. For the WebAssembly - // target, however, we'll lower in some cases to intrinsic calls instead - // which may trap. If we detect that this is a situation where we'll be - // using the intrinsics then we report that the call map trap, which - // callers might need to handle. - if !self.wasm_and_missing_nontrapping_fptoint() { - return false; - } - let src_ty = self.cx.val_ty(val); - let float_width = self.cx.float_width(src_ty); - let int_width = self.cx.int_width(dest_ty); - matches!((int_width, float_width), (32, 32) | (32, 64) | (64, 32) | (64, 64)) + None } fn fptoui(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value { - // When we can, use the native wasm intrinsics which have tighter - // codegen. Note that this has a semantic difference in that the - // intrinsic can trap whereas `fptoui` never traps. That difference, - // however, is handled by `fptosui_may_trap` above. + // On WebAssembly the `fptoui` and `fptosi` instructions currently have + // poor codegen. The reason for this is that the corresponding wasm + // instructions, `i32.trunc_f32_s` for example, will trap when the float + // is out-of-bounds, infinity, or nan. This means that LLVM + // automatically inserts control flow around `fptoui` and `fptosi` + // because the LLVM instruction `fptoui` is defined as producing a + // poison value, not having UB on out-of-bounds values. // - // Note that we skip the wasm intrinsics for vector types where `fptoui` - // must be used instead. - if self.wasm_and_missing_nontrapping_fptoint() { + // This method, however, is only used with non-saturating casts that + // have UB on out-of-bounds values. This means that it's ok if we use + // the raw wasm instruction since out-of-bounds values can do whatever + // we like. To ensure that LLVM picks the right instruction we choose + // the raw wasm intrinsic functions which avoid LLVM inserting all the + // other control flow automatically. + if self.sess().target.arch == "wasm32" { let src_ty = self.cx.val_ty(val); if self.cx.type_kind(src_ty) != TypeKind::Vector { let float_width = self.cx.float_width(src_ty); @@ -765,7 +732,8 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } fn fptosi(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value { - if self.wasm_and_missing_nontrapping_fptoint() { + // see `fptoui` above for why wasm is different here + if self.sess().target.arch == "wasm32" { let src_ty = self.cx.val_ty(val); if self.cx.type_kind(src_ty) != TypeKind::Vector { let float_width = self.cx.float_width(src_ty); @@ -1419,9 +1387,4 @@ impl Builder<'a, 'll, 'tcx> { llvm::LLVMAddIncoming(phi, &val, &bb, 1 as c_uint); } } - - fn wasm_and_missing_nontrapping_fptoint(&self) -> bool { - self.sess().target.arch == "wasm32" - && !self.sess().target_features.contains(&sym::nontrapping_dash_fptoint) - } } diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 773c0c16328e7..f5c54b11c08e7 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -503,14 +503,6 @@ impl CodegenCx<'b, 'tcx> { let t_f32 = self.type_f32(); let t_f64 = self.type_f64(); - ifn!("llvm.wasm.trunc.saturate.unsigned.i32.f32", fn(t_f32) -> t_i32); - ifn!("llvm.wasm.trunc.saturate.unsigned.i32.f64", fn(t_f64) -> t_i32); - ifn!("llvm.wasm.trunc.saturate.unsigned.i64.f32", fn(t_f32) -> t_i64); - ifn!("llvm.wasm.trunc.saturate.unsigned.i64.f64", fn(t_f64) -> t_i64); - ifn!("llvm.wasm.trunc.saturate.signed.i32.f32", fn(t_f32) -> t_i32); - ifn!("llvm.wasm.trunc.saturate.signed.i32.f64", fn(t_f64) -> t_i32); - ifn!("llvm.wasm.trunc.saturate.signed.i64.f32", fn(t_f32) -> t_i64); - ifn!("llvm.wasm.trunc.saturate.signed.i64.f64", fn(t_f64) -> t_i64); ifn!("llvm.wasm.trunc.unsigned.i32.f32", fn(t_f32) -> t_i32); ifn!("llvm.wasm.trunc.unsigned.i32.f64", fn(t_f64) -> t_i32); ifn!("llvm.wasm.trunc.unsigned.i64.f32", fn(t_f32) -> t_i64); @@ -520,6 +512,28 @@ impl CodegenCx<'b, 'tcx> { ifn!("llvm.wasm.trunc.signed.i64.f32", fn(t_f32) -> t_i64); ifn!("llvm.wasm.trunc.signed.i64.f64", fn(t_f64) -> t_i64); + ifn!("llvm.fptosi.sat.i8.f32", fn(t_f32) -> t_i8); + ifn!("llvm.fptosi.sat.i16.f32", fn(t_f32) -> t_i16); + ifn!("llvm.fptosi.sat.i32.f32", fn(t_f32) -> t_i32); + ifn!("llvm.fptosi.sat.i64.f32", fn(t_f32) -> t_i64); + ifn!("llvm.fptosi.sat.i128.f32", fn(t_f32) -> t_i128); + ifn!("llvm.fptosi.sat.i8.f64", fn(t_f64) -> t_i8); + ifn!("llvm.fptosi.sat.i16.f64", fn(t_f64) -> t_i16); + ifn!("llvm.fptosi.sat.i32.f64", fn(t_f64) -> t_i32); + ifn!("llvm.fptosi.sat.i64.f64", fn(t_f64) -> t_i64); + ifn!("llvm.fptosi.sat.i128.f64", fn(t_f64) -> t_i128); + + ifn!("llvm.fptoui.sat.i8.f32", fn(t_f32) -> t_i8); + ifn!("llvm.fptoui.sat.i16.f32", fn(t_f32) -> t_i16); + ifn!("llvm.fptoui.sat.i32.f32", fn(t_f32) -> t_i32); + ifn!("llvm.fptoui.sat.i64.f32", fn(t_f32) -> t_i64); + ifn!("llvm.fptoui.sat.i128.f32", fn(t_f32) -> t_i128); + ifn!("llvm.fptoui.sat.i8.f64", fn(t_f64) -> t_i8); + ifn!("llvm.fptoui.sat.i16.f64", fn(t_f64) -> t_i16); + ifn!("llvm.fptoui.sat.i32.f64", fn(t_f64) -> t_i32); + ifn!("llvm.fptoui.sat.i64.f64", fn(t_f64) -> t_i64); + ifn!("llvm.fptoui.sat.i128.f64", fn(t_f64) -> t_i128); + ifn!("llvm.trap", fn() -> void); ifn!("llvm.debugtrap", fn() -> void); ifn!("llvm.frameaddress", fn(t_i32) -> i8p); diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 629cb64d43ee1..9917c23f12150 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -11,7 +11,7 @@ use rustc_apfloat::{ieee, Float, Round, Status}; use rustc_hir::lang_items::LangItem; use rustc_middle::mir; use rustc_middle::ty::cast::{CastTy, IntTy}; -use rustc_middle::ty::layout::{HasTyCtxt, TyAndLayout}; +use rustc_middle::ty::layout::HasTyCtxt; use rustc_middle::ty::{self, adjustment::PointerCast, Instance, Ty, TyCtxt}; use rustc_span::source_map::{Span, DUMMY_SP}; use rustc_span::symbol::sym; @@ -385,10 +385,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.inttoptr(usize_llval, ll_t_out) } (CastTy::Float, CastTy::Int(IntTy::I)) => { - cast_float_to_int(&mut bx, true, llval, ll_t_in, ll_t_out, cast) + cast_float_to_int(&mut bx, true, llval, ll_t_in, ll_t_out) } (CastTy::Float, CastTy::Int(_)) => { - cast_float_to_int(&mut bx, false, llval, ll_t_in, ll_t_out, cast) + cast_float_to_int(&mut bx, false, llval, ll_t_in, ll_t_out) } _ => bug!("unsupported cast: {:?} to {:?}", operand.layout.ty, cast.ty), }; @@ -790,7 +790,6 @@ fn cast_float_to_int<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( x: Bx::Value, float_ty: Bx::Type, int_ty: Bx::Type, - int_layout: TyAndLayout<'tcx>, ) -> Bx::Value { if let Some(false) = bx.cx().sess().opts.debugging_opts.saturating_float_casts { return if signed { bx.fptosi(x, int_ty) } else { bx.fptoui(x, int_ty) }; @@ -891,134 +890,39 @@ fn cast_float_to_int<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let int_min = bx.cx().const_uint_big(int_ty, int_min(signed, int_width) as u128); let zero = bx.cx().const_uint(int_ty, 0); - // The codegen here differs quite a bit depending on whether our builder's - // `fptosi` and `fptoui` instructions may trap for out-of-bounds values. If - // they don't trap then we can start doing everything inline with a - // `select` instruction because it's ok to execute `fptosi` and `fptoui` - // even if we don't use the results. - if !bx.fptosui_may_trap(x, int_ty) { - // Step 1 ... - let fptosui_result = if signed { bx.fptosi(x, int_ty) } else { bx.fptoui(x, int_ty) }; - let less_or_nan = bx.fcmp(RealPredicate::RealULT, x, f_min); - let greater = bx.fcmp(RealPredicate::RealOGT, x, f_max); - - // Step 2: We use two comparisons and two selects, with %s1 being the - // result: - // %less_or_nan = fcmp ult %x, %f_min - // %greater = fcmp olt %x, %f_max - // %s0 = select %less_or_nan, int_ty::MIN, %fptosi_result - // %s1 = select %greater, int_ty::MAX, %s0 - // Note that %less_or_nan uses an *unordered* comparison. This - // comparison is true if the operands are not comparable (i.e., if x is - // NaN). The unordered comparison ensures that s1 becomes int_ty::MIN if - // x is NaN. - // - // Performance note: Unordered comparison can be lowered to a "flipped" - // comparison and a negation, and the negation can be merged into the - // select. Therefore, it not necessarily any more expensive than a - // ordered ("normal") comparison. Whether these optimizations will be - // performed is ultimately up to the backend, but at least x86 does - // perform them. - let s0 = bx.select(less_or_nan, int_min, fptosui_result); - let s1 = bx.select(greater, int_max, s0); - - // Step 3: NaN replacement. - // For unsigned types, the above step already yielded int_ty::MIN == 0 if x is NaN. - // Therefore we only need to execute this step for signed integer types. - if signed { - // LLVM has no isNaN predicate, so we use (x == x) instead - let cmp = bx.fcmp(RealPredicate::RealOEQ, x, x); - bx.select(cmp, s1, zero) - } else { - s1 - } + // Step 1 ... + let fptosui_result = if signed { bx.fptosi(x, int_ty) } else { bx.fptoui(x, int_ty) }; + let less_or_nan = bx.fcmp(RealPredicate::RealULT, x, f_min); + let greater = bx.fcmp(RealPredicate::RealOGT, x, f_max); + + // Step 2: We use two comparisons and two selects, with %s1 being the + // result: + // %less_or_nan = fcmp ult %x, %f_min + // %greater = fcmp olt %x, %f_max + // %s0 = select %less_or_nan, int_ty::MIN, %fptosi_result + // %s1 = select %greater, int_ty::MAX, %s0 + // Note that %less_or_nan uses an *unordered* comparison. This + // comparison is true if the operands are not comparable (i.e., if x is + // NaN). The unordered comparison ensures that s1 becomes int_ty::MIN if + // x is NaN. + // + // Performance note: Unordered comparison can be lowered to a "flipped" + // comparison and a negation, and the negation can be merged into the + // select. Therefore, it not necessarily any more expensive than a + // ordered ("normal") comparison. Whether these optimizations will be + // performed is ultimately up to the backend, but at least x86 does + // perform them. + let s0 = bx.select(less_or_nan, int_min, fptosui_result); + let s1 = bx.select(greater, int_max, s0); + + // Step 3: NaN replacement. + // For unsigned types, the above step already yielded int_ty::MIN == 0 if x is NaN. + // Therefore we only need to execute this step for signed integer types. + if signed { + // LLVM has no isNaN predicate, so we use (x == x) instead + let cmp = bx.fcmp(RealPredicate::RealOEQ, x, x); + bx.select(cmp, s1, zero) } else { - // In this case we cannot execute `fptosi` or `fptoui` and then later - // discard the result. The builder is telling us that these instructions - // will trap on out-of-bounds values, so we need to use basic blocks and - // control flow to avoid executing the `fptosi` and `fptoui` - // instructions. - // - // The general idea of what we're constructing here is, for f64 -> i32: - // - // ;; block so far... %0 is the argument - // %result = alloca i32, align 4 - // %inbound_lower = fcmp oge double %0, 0xC1E0000000000000 - // %inbound_upper = fcmp ole double %0, 0x41DFFFFFFFC00000 - // ;; match (inbound_lower, inbound_upper) { - // ;; (true, true) => %0 can be converted without trapping - // ;; (false, false) => %0 is a NaN - // ;; (true, false) => %0 is too large - // ;; (false, true) => %0 is too small - // ;; } - // ;; - // ;; The (true, true) check, go to %convert if so. - // %inbounds = and i1 %inbound_lower, %inbound_upper - // br i1 %inbounds, label %convert, label %specialcase - // - // convert: - // %cvt = call i32 @llvm.wasm.trunc.signed.i32.f64(double %0) - // store i32 %cvt, i32* %result, align 4 - // br label %done - // - // specialcase: - // ;; Handle the cases where the number is NaN, too large or too small - // - // ;; Either (true, false) or (false, true) - // %is_not_nan = or i1 %inbound_lower, %inbound_upper - // ;; Figure out which saturated value we are interested in if not `NaN` - // %saturated = select i1 %inbound_lower, i32 2147483647, i32 -2147483648 - // ;; Figure out between saturated and NaN representations - // %result_nan = select i1 %is_not_nan, i32 %saturated, i32 0 - // store i32 %result_nan, i32* %result, align 4 - // br label %done - // - // done: - // %r = load i32, i32* %result, align 4 - // ;; ... - let done = bx.build_sibling_block("float_cast_done"); - let mut convert = bx.build_sibling_block("float_cast_convert"); - let mut specialcase = bx.build_sibling_block("float_cast_specialcase"); - - let result = PlaceRef::alloca(bx, int_layout); - result.storage_live(bx); - - // Use control flow to figure out whether we can execute `fptosi` in a - // basic block, or whether we go to a different basic block to implement - // the saturating logic. - let inbound_lower = bx.fcmp(RealPredicate::RealOGE, x, f_min); - let inbound_upper = bx.fcmp(RealPredicate::RealOLE, x, f_max); - let inbounds = bx.and(inbound_lower, inbound_upper); - bx.cond_br(inbounds, convert.llbb(), specialcase.llbb()); - - // Translation of the `convert` basic block - let cvt = if signed { convert.fptosi(x, int_ty) } else { convert.fptoui(x, int_ty) }; - convert.store(cvt, result.llval, result.align); - convert.br(done.llbb()); - - // Translation of the `specialcase` basic block. Note that like above - // we try to be a bit clever here for unsigned conversions. In those - // cases the `int_min` is zero so we don't need two select instructions, - // just one to choose whether we need `int_max` or not. If - // `inbound_lower` is true then we're guaranteed to not be `NaN` and - // since we're greater than zero we must be saturating to `int_max`. If - // `inbound_lower` is false then we're either NaN or less than zero, so - // we saturate to zero. - let result_nan = if signed { - let is_not_nan = specialcase.or(inbound_lower, inbound_upper); - let saturated = specialcase.select(inbound_lower, int_max, int_min); - specialcase.select(is_not_nan, saturated, zero) - } else { - specialcase.select(inbound_lower, int_max, int_min) - }; - specialcase.store(result_nan, result.llval, result.align); - specialcase.br(done.llbb()); - - // Translation of the `done` basic block, positioning ourselves to - // continue from that point as well. - *bx = done; - let ret = bx.load(result.llval, result.align); - result.storage_dead(bx); - ret + s1 } } diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index d5bd2780388e6..1bc05f30e5c37 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -171,7 +171,6 @@ pub trait BuilderMethods<'a, 'tcx>: fn sext(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value; fn fptoui_sat(&mut self, val: Self::Value, dest_ty: Self::Type) -> Option; fn fptosi_sat(&mut self, val: Self::Value, dest_ty: Self::Type) -> Option; - fn fptosui_may_trap(&self, val: Self::Value, dest_ty: Self::Type) -> bool; fn fptoui(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value; fn fptosi(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value; fn uitofp(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value; diff --git a/compiler/rustc_error_codes/src/error_codes/E0404.md b/compiler/rustc_error_codes/src/error_codes/E0404.md index 1360cc99afcc4..d6fa51e618c4c 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0404.md +++ b/compiler/rustc_error_codes/src/error_codes/E0404.md @@ -8,14 +8,15 @@ struct Foo; struct Bar; impl Foo for Bar {} // error: `Foo` is not a trait +fn baz(t: T) {} // error: `Foo` is not a trait ``` Another erroneous code example: ```compile_fail,E0404 -struct Foo; +type Foo = Iterator; -fn bar(t: T) {} // error: `Foo` is not a trait +fn bar(t: T) {} // error: `Foo` is a type alias ``` Please verify that the trait's name was not misspelled or that the right @@ -30,14 +31,27 @@ struct Bar; impl Foo for Bar { // ok! // functions implementation } + +fn baz(t: T) {} // ok! ``` -or: +Alternatively, you could introduce a new trait with your desired restrictions +as a super trait: ``` -trait Foo { - // some functions -} +# trait Foo {} +# struct Bar; +# impl Foo for Bar {} +trait Qux: Foo {} // Anything that implements Qux also needs to implement Foo +fn baz(t: T) {} // also ok! +``` + +Finally, if you are on nightly and want to use a trait alias +instead of a type alias, you should use `#![feature(trait_alias)]`: + +``` +#![feature(trait_alias)] +trait Foo = Iterator; fn bar(t: T) {} // ok! ``` diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index a410826d3fda6..cb0615746012f 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -63,6 +63,10 @@ macro_rules! declare_features { _ => panic!("`{}` was not listed in `declare_features`", feature), } } + + pub fn unordered_const_ty_params(&self) -> bool { + self.const_generics || self.const_generics_defaults + } } }; } diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 1051fb8cea279..a70be14546b41 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -296,7 +296,9 @@ impl GenericArg<'_> { match self { GenericArg::Lifetime(_) => ast::ParamKindOrd::Lifetime, GenericArg::Type(_) => ast::ParamKindOrd::Type, - GenericArg::Const(_) => ast::ParamKindOrd::Const { unordered: feats.const_generics }, + GenericArg::Const(_) => { + ast::ParamKindOrd::Const { unordered: feats.unordered_const_ty_params() } + } } } } diff --git a/compiler/rustc_middle/src/middle/region.rs b/compiler/rustc_middle/src/middle/region.rs index 5440e63543d40..f44267a404bf3 100644 --- a/compiler/rustc_middle/src/middle/region.rs +++ b/compiler/rustc_middle/src/middle/region.rs @@ -235,18 +235,6 @@ pub struct ScopeTree { /// escape into 'static and should have no local cleanup scope. rvalue_scopes: FxHashMap>, - /// Encodes the hierarchy of fn bodies. Every fn body (including - /// closures) forms its own distinct region hierarchy, rooted in - /// the block that is the fn body. This map points from the ID of - /// that root block to the ID of the root block for the enclosing - /// fn, if any. Thus the map structures the fn bodies into a - /// hierarchy based on their lexical mapping. This is used to - /// handle the relationships between regions in a fn and in a - /// closure defined by that fn. See the "Modeling closures" - /// section of the README in infer::region_constraints for - /// more details. - closure_tree: FxHashMap, - /// If there are any `yield` nested within a scope, this map /// stores the `Span` of the last one and its index in the /// postorder of the Visitor traversal on the HIR. @@ -356,23 +344,6 @@ impl ScopeTree { self.destruction_scopes.get(&n).cloned() } - /// Records that `sub_closure` is defined within `sup_closure`. These IDs - /// should be the ID of the block that is the fn body, which is - /// also the root of the region hierarchy for that fn. - pub fn record_closure_parent( - &mut self, - sub_closure: hir::ItemLocalId, - sup_closure: hir::ItemLocalId, - ) { - debug!( - "record_closure_parent(sub_closure={:?}, sup_closure={:?})", - sub_closure, sup_closure - ); - assert!(sub_closure != sup_closure); - let previous = self.closure_tree.insert(sub_closure, sup_closure); - assert!(previous.is_none()); - } - pub fn record_var_scope(&mut self, var: hir::ItemLocalId, lifetime: Scope) { debug!("record_var_scope(sub={:?}, sup={:?})", var, lifetime); assert!(var != lifetime.item_local_id()); @@ -474,7 +445,6 @@ impl<'a> HashStable> for ScopeTree { ref var_map, ref destruction_scopes, ref rvalue_scopes, - ref closure_tree, ref yield_in_scope, } = *self; @@ -488,7 +458,6 @@ impl<'a> HashStable> for ScopeTree { var_map.hash_stable(hcx, hasher); destruction_scopes.hash_stable(hcx, hasher); rvalue_scopes.hash_stable(hcx, hasher); - closure_tree.hash_stable(hcx, hasher); yield_in_scope.hash_stable(hcx, hasher); } } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index a1a56eb1b5b44..08fa12aa3718f 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -564,7 +564,7 @@ rustc_queries! { } /// Collects the associated items defined on a trait or impl. - query associated_items(key: DefId) -> ty::AssociatedItems<'tcx> { + query associated_items(key: DefId) -> ty::AssocItems<'tcx> { storage(ArenaCacheSelector<'tcx>) desc { |tcx| "collecting associated items of {}", tcx.def_path_str(key) } } diff --git a/compiler/rustc_middle/src/ty/assoc.rs b/compiler/rustc_middle/src/ty/assoc.rs index d3770fa416b53..d005f63ed4383 100644 --- a/compiler/rustc_middle/src/ty/assoc.rs +++ b/compiler/rustc_middle/src/ty/assoc.rs @@ -96,15 +96,15 @@ impl AssocKind { /// it is relatively expensive. Instead, items are indexed by `Symbol` and hygienic comparison is /// done only on items with the same name. #[derive(Debug, Clone, PartialEq, HashStable)] -pub struct AssociatedItems<'tcx> { +pub struct AssocItems<'tcx> { pub(super) items: SortedIndexMultiMap, } -impl<'tcx> AssociatedItems<'tcx> { +impl<'tcx> AssocItems<'tcx> { /// Constructs an `AssociatedItems` map from a series of `ty::AssocItem`s in definition order. pub fn new(items_in_def_order: impl IntoIterator) -> Self { let items = items_in_def_order.into_iter().map(|item| (item.ident.name, item)).collect(); - AssociatedItems { items } + AssocItems { items } } /// Returns a slice of associated items in the order they were defined. diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs index d30a8693959f3..c8fdbc30d1591 100644 --- a/compiler/rustc_middle/src/ty/generics.rs +++ b/compiler/rustc_middle/src/ty/generics.rs @@ -36,7 +36,7 @@ impl GenericParamDefKind { GenericParamDefKind::Lifetime => ast::ParamKindOrd::Lifetime, GenericParamDefKind::Type { .. } => ast::ParamKindOrd::Type, GenericParamDefKind::Const { .. } => { - ast::ParamKindOrd::Const { unordered: tcx.features().const_generics } + ast::ParamKindOrd::Const { unordered: tcx.features().unordered_const_ty_params() } } } } diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index c63edf365a1aa..70b019e8468e7 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -3,6 +3,7 @@ // from live codes are live, and everything else is dead. use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; @@ -15,7 +16,7 @@ use rustc_middle::middle::privacy; use rustc_middle::ty::{self, DefIdTree, TyCtxt}; use rustc_session::lint; -use rustc_span::symbol::{sym, Symbol}; +use rustc_span::symbol::{sym, Ident, Symbol}; // Any local node that may call something in its body block should be // explored. For example, if it's a live Node::Item that is a @@ -505,6 +506,13 @@ fn find_live<'tcx>( symbol_visitor.live_symbols } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +enum ExtraNote { + /// Use this to provide some examples in the diagnostic of potential other purposes for a value + /// or field that is dead code + OtherPurposeExamples, +} + struct DeadVisitor<'tcx> { tcx: TyCtxt<'tcx>, live_symbols: FxHashSet, @@ -570,14 +578,42 @@ impl DeadVisitor<'tcx> { &mut self, id: hir::HirId, span: rustc_span::Span, - name: Symbol, + name: Ident, participle: &str, + extra_note: Option, ) { if !name.as_str().starts_with('_') { self.tcx.struct_span_lint_hir(lint::builtin::DEAD_CODE, id, span, |lint| { let def_id = self.tcx.hir().local_def_id(id); let descr = self.tcx.def_kind(def_id).descr(def_id.to_def_id()); - lint.build(&format!("{} is never {}: `{}`", descr, participle, name)).emit() + + let prefixed = vec![(name.span, format!("_{}", name))]; + + let mut diag = + lint.build(&format!("{} is never {}: `{}`", descr, participle, name)); + + diag.multipart_suggestion( + "if this is intentional, prefix it with an underscore", + prefixed, + Applicability::MachineApplicable, + ); + + let mut note = format!( + "the leading underscore signals that this {} serves some other \ + purpose even if it isn't used in a way that we can detect.", + descr, + ); + if matches!(extra_note, Some(ExtraNote::OtherPurposeExamples)) { + note += " (e.g. for its effect when dropped or in foreign code)"; + } + + diag.note(¬e); + + // Force the note we added to the front, before any other subdiagnostics + // added in lint.build(...) + diag.children.rotate_right(1); + + diag.emit() }); } } @@ -623,7 +659,7 @@ impl Visitor<'tcx> for DeadVisitor<'tcx> { hir::ItemKind::Struct(..) => "constructed", // Issue #52325 _ => "used", }; - self.warn_dead_code(item.hir_id(), span, item.ident.name, participle); + self.warn_dead_code(item.hir_id(), span, item.ident, participle, None); } else { // Only continue if we didn't warn intravisit::walk_item(self, item); @@ -637,7 +673,7 @@ impl Visitor<'tcx> for DeadVisitor<'tcx> { id: hir::HirId, ) { if self.should_warn_about_variant(&variant) { - self.warn_dead_code(variant.id, variant.span, variant.ident.name, "constructed"); + self.warn_dead_code(variant.id, variant.span, variant.ident, "constructed", None); } else { intravisit::walk_variant(self, variant, g, id); } @@ -645,14 +681,20 @@ impl Visitor<'tcx> for DeadVisitor<'tcx> { fn visit_foreign_item(&mut self, fi: &'tcx hir::ForeignItem<'tcx>) { if self.should_warn_about_foreign_item(fi) { - self.warn_dead_code(fi.hir_id(), fi.span, fi.ident.name, "used"); + self.warn_dead_code(fi.hir_id(), fi.span, fi.ident, "used", None); } intravisit::walk_foreign_item(self, fi); } fn visit_field_def(&mut self, field: &'tcx hir::FieldDef<'tcx>) { if self.should_warn_about_field(&field) { - self.warn_dead_code(field.hir_id, field.span, field.ident.name, "read"); + self.warn_dead_code( + field.hir_id, + field.span, + field.ident, + "read", + Some(ExtraNote::OtherPurposeExamples), + ); } intravisit::walk_field_def(self, field); } @@ -664,8 +706,9 @@ impl Visitor<'tcx> for DeadVisitor<'tcx> { self.warn_dead_code( impl_item.hir_id(), impl_item.span, - impl_item.ident.name, + impl_item.ident, "used", + None, ); } self.visit_nested_body(body_id) @@ -683,7 +726,7 @@ impl Visitor<'tcx> for DeadVisitor<'tcx> { } else { impl_item.ident.span }; - self.warn_dead_code(impl_item.hir_id(), span, impl_item.ident.name, "used"); + self.warn_dead_code(impl_item.hir_id(), span, impl_item.ident, "used", None); } self.visit_nested_body(body_id) } diff --git a/compiler/rustc_passes/src/region.rs b/compiler/rustc_passes/src/region.rs index b532021bed2e9..14a373c59423f 100644 --- a/compiler/rustc_passes/src/region.rs +++ b/compiler/rustc_passes/src/region.rs @@ -23,14 +23,6 @@ use std::mem; #[derive(Debug, Copy, Clone)] pub struct Context { - /// The root of the current region tree. This is typically the id - /// of the innermost fn body. Each fn forms its own disjoint tree - /// in the region hierarchy. These fn bodies are themselves - /// arranged into a tree. See the "Modeling closures" section of - /// the README in `rustc_trait_selection::infer::region_constraints` - /// for more details. - root_id: Option, - /// The scope that contains any new variables declared, plus its depth in /// the scope tree. var_parent: Option<(Scope, ScopeDepth)>, @@ -743,11 +735,6 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> { let outer_pessimistic_yield = mem::replace(&mut self.pessimistic_yield, false); self.terminating_scopes.insert(body.value.hir_id.local_id); - if let Some(root_id) = self.cx.root_id { - self.scope_tree.record_closure_parent(body.value.hir_id.local_id, root_id); - } - self.cx.root_id = Some(body.value.hir_id.local_id); - self.enter_scope(Scope { id: body.value.hir_id.local_id, data: ScopeData::CallSite }); self.enter_scope(Scope { id: body.value.hir_id.local_id, data: ScopeData::Arguments }); @@ -824,7 +811,7 @@ fn region_scope_tree(tcx: TyCtxt<'_>, def_id: DefId) -> &ScopeTree { tcx, scope_tree: ScopeTree::default(), expr_and_pat_count: 0, - cx: Context { root_id: None, parent: None, var_parent: None }, + cx: Context { parent: None, var_parent: None }, terminating_scopes: Default::default(), pessimistic_yield: false, fixup_scopes: vec![], diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 87e28f7fcc592..c5f12c0c691b3 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -472,17 +472,6 @@ impl<'a> Resolver<'a> { ); err } - ResolutionError::ParamInAnonConstInTyDefault(name) => { - let mut err = self.session.struct_span_err( - span, - "constant values inside of type parameter defaults must not depend on generic parameters", - ); - err.span_label( - span, - format!("the anonymous constant must not depend on the parameter `{}`", name), - ); - err - } ResolutionError::ParamInNonTrivialAnonConst { name, is_type } => { let mut err = self.session.struct_span_err( span, diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 9321f11f65933..92f21191de430 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -555,18 +555,23 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { // provide previous type parameters as they're built. We // put all the parameters on the ban list and then remove // them one by one as they are processed and become available. - let mut default_ban_rib = Rib::new(ForwardGenericParamBanRibKind); - let mut found_default = false; - default_ban_rib.bindings.extend(generics.params.iter().filter_map( - |param| match param.kind { - GenericParamKind::Type { default: Some(_), .. } - | GenericParamKind::Const { default: Some(_), .. } => { - found_default = true; - Some((Ident::with_dummy_span(param.ident.name), Res::Err)) + let mut forward_ty_ban_rib = Rib::new(ForwardGenericParamBanRibKind); + let mut forward_const_ban_rib = Rib::new(ForwardGenericParamBanRibKind); + for param in generics.params.iter() { + match param.kind { + GenericParamKind::Type { .. } => { + forward_ty_ban_rib + .bindings + .insert(Ident::with_dummy_span(param.ident.name), Res::Err); } - _ => None, - }, - )); + GenericParamKind::Const { .. } => { + forward_const_ban_rib + .bindings + .insert(Ident::with_dummy_span(param.ident.name), Res::Err); + } + GenericParamKind::Lifetime => {} + } + } // rust-lang/rust#61631: The type `Self` is essentially // another type parameter. For ADTs, we consider it @@ -579,7 +584,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { // such as in the case of `trait Add`.) if self.diagnostic_metadata.current_self_item.is_some() { // (`Some` if + only if we are in ADT's generics.) - default_ban_rib.bindings.insert(Ident::with_dummy_span(kw::SelfUpper), Res::Err); + forward_ty_ban_rib.bindings.insert(Ident::with_dummy_span(kw::SelfUpper), Res::Err); } for param in &generics.params { @@ -591,32 +596,38 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { } if let Some(ref ty) = default { - self.ribs[TypeNS].push(default_ban_rib); - self.with_rib(ValueNS, ForwardGenericParamBanRibKind, |this| { - // HACK: We use an empty `ForwardGenericParamBanRibKind` here which - // is only used to forbid the use of const parameters inside of - // type defaults. - // - // While the rib name doesn't really fit here, it does allow us to use the same - // code for both const and type parameters. - this.visit_ty(ty); - }); - default_ban_rib = self.ribs[TypeNS].pop().unwrap(); + self.ribs[TypeNS].push(forward_ty_ban_rib); + self.ribs[ValueNS].push(forward_const_ban_rib); + self.visit_ty(ty); + forward_const_ban_rib = self.ribs[ValueNS].pop().unwrap(); + forward_ty_ban_rib = self.ribs[TypeNS].pop().unwrap(); } // Allow all following defaults to refer to this type parameter. - default_ban_rib.bindings.remove(&Ident::with_dummy_span(param.ident.name)); + forward_ty_ban_rib.bindings.remove(&Ident::with_dummy_span(param.ident.name)); } - GenericParamKind::Const { ref ty, kw_span: _, default: _ } => { - // FIXME(const_generics_defaults): handle `default` value here - for bound in ¶m.bounds { - self.visit_param_bound(bound); - } + GenericParamKind::Const { ref ty, kw_span: _, ref default } => { + // Const parameters can't have param bounds. + assert!(param.bounds.is_empty()); + self.ribs[TypeNS].push(Rib::new(ConstParamTyRibKind)); self.ribs[ValueNS].push(Rib::new(ConstParamTyRibKind)); self.visit_ty(ty); self.ribs[TypeNS].pop().unwrap(); self.ribs[ValueNS].pop().unwrap(); + + if let Some(ref expr) = default { + self.ribs[TypeNS].push(forward_ty_ban_rib); + self.ribs[ValueNS].push(forward_const_ban_rib); + self.visit_anon_const(expr); + forward_const_ban_rib = self.ribs[ValueNS].pop().unwrap(); + forward_ty_ban_rib = self.ribs[TypeNS].pop().unwrap(); + } + + // Allow all following defaults to refer to this const parameter. + forward_const_ban_rib + .bindings + .remove(&Ident::with_dummy_span(param.ident.name)); } } } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 6fae6921fc9b2..e33c374f562e2 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -930,7 +930,14 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { let msg = "you might have meant to use `#![feature(trait_alias)]` instead of a \ `type` alias"; if let Some(span) = self.def_span(def_id) { - err.span_help(span, msg); + if let Ok(snip) = self.r.session.source_map().span_to_snippet(span) { + // The span contains a type alias so we should be able to + // replace `type` with `trait`. + let snip = snip.replacen("type", "trait", 1); + err.span_suggestion(span, msg, snip, Applicability::MaybeIncorrect); + } else { + err.span_help(span, msg); + } } else { err.help(msg); } diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs index fc1ea4ec84629..174df09cbdbb2 100644 --- a/compiler/rustc_resolve/src/late/lifetimes.rs +++ b/compiler/rustc_resolve/src/late/lifetimes.rs @@ -165,29 +165,6 @@ crate struct LifetimeContext<'a, 'tcx> { map: &'a mut NamedRegionMap, scope: ScopeRef<'a>, - /// This is slightly complicated. Our representation for poly-trait-refs contains a single - /// binder and thus we only allow a single level of quantification. However, - /// the syntax of Rust permits quantification in two places, e.g., `T: for <'a> Foo<'a>` - /// and `for <'a, 'b> &'b T: Foo<'a>`. In order to get the De Bruijn indices - /// correct when representing these constraints, we should only introduce one - /// scope. However, we want to support both locations for the quantifier and - /// during lifetime resolution we want precise information (so we can't - /// desugar in an earlier phase). Moreso, an error here doesn't cause a bail - /// from type checking, so we need to be extra careful that we don't lose - /// any bound var information. - /// - /// So, if we encounter a quantifier at the outer scope, we set - /// `trait_ref_hack` to the hir id of the bounded type (and introduce a scope). - /// Then, if we encounter a quantifier at the inner scope, then we know to - /// emit an error. Importantly though, we do have to track the lifetimes - /// defined on the outer scope (i.e. the bounded ty), since we continue - /// to type check after emitting an error; we therefore assume that the bound - /// vars on the inner trait refs come from both quantifiers. - /// - /// If we encounter a quantifier in the inner scope `trait_ref_hack` being - /// `None`, then we just introduce the scope at the inner quantifier as normal. - trait_ref_hack: Option, - /// Used to disallow the use of in-band lifetimes in `fn` or `Fn` syntax. is_in_fn_syntax: bool, @@ -244,10 +221,7 @@ enum Scope<'a> { /// of the resulting opaque type. opaque_type_parent: bool, - /// True only if this `Binder` scope is from the quantifiers on a - /// `PolyTraitRef`. This is necessary for `associated_type_bounds`, which - /// requires binders of nested trait refs to be merged. - from_poly_trait_ref: bool, + scope_type: BinderScopeType, /// The late bound vars for a given item are stored by `HirId` to be /// queried later. However, if we enter an elision scope, we have to @@ -282,41 +256,6 @@ enum Scope<'a> { s: ScopeRef<'a>, }, - /// This is a particularly interesting consequence of how we handle poly - /// trait refs. See `trait_ref_hack` for additional info. This bit is - /// important w.r.t. querying late-bound vars. - /// - /// To completely understand why this is necessary, first it's important to - /// realize that `T: for<'a> U + for<'a, 'b> V` is actually two separate - /// trait refs: `T: for<'a> U` and `T: for<'b> V` and as such, the late - /// bound vars on each needs to be tracked separately. Also, in this case, - /// are *three* relevant `HirId`s: one for the entire bound and one - /// for each separate one. - /// - /// Next, imagine three different poly trait refs: - /// 1) `for<'a, 'b> T: U<'a, 'b>` - /// 2) `T: for<'a, 'b> U<'a, 'b>` - /// 3) `for<'a> T: for<'b> U<'a, 'b>` - /// - /// First, note that the third example is semantically invalid and an error, - /// but we *must* handle it as valid, since type checking isn't bailed out - /// of. Other than that, if ask for bound vars for each, we expect - /// `['a, 'b]`. If we *didn't* allow binders before `T`, then we would - /// always introduce a binder scope at the inner trait ref. This is great, - /// because later on during type-checking, we will ask "what are the late - /// bound vars on this trait ref". However, because we allow bound vars on - /// the bound itself, we have to have some way of keeping track of the fact - /// that we actually want to store the late bound vars as being associated - /// with the trait ref; this is that. - /// - /// One alternative way to handle this would be to just introduce a new - /// `Binder` scope, but that's semantically a bit different, since bound - /// vars from both `for<...>`s *do* share the same binder level. - TraitRefHackInner { - hir_id: hir::HirId, - s: ScopeRef<'a>, - }, - /// When we have nested trait refs, we concanetate late bound vars for inner /// trait refs from outer ones. But we also need to include any HRTB /// lifetimes encountered when identifying the trait that an associated type @@ -333,6 +272,22 @@ enum Scope<'a> { Root, } +#[derive(Copy, Clone, Debug)] +enum BinderScopeType { + /// Any non-concatenating binder scopes. + Normal, + /// Within a syntactic trait ref, there may be multiple poly trait refs that + /// are nested (under the `associcated_type_bounds` feature). The binders of + /// the innner poly trait refs are extended from the outer poly trait refs + /// and don't increase the late bound depth. If you had + /// `T: for<'a> Foo Baz<'a, 'b>>`, then the `for<'b>` scope + /// would be `Concatenating`. This also used in trait refs in where clauses + /// where we have two binders `for<> T: for<> Foo` (I've intentionally left + /// out any lifetimes because they aren't needed to show the two scopes). + /// The inner `for<>` has a scope of `Concatenating`. + Concatenating, +} + // A helper struct for debugging scopes without printing parent scopes struct TruncatedScopeDebug<'a>(&'a Scope<'a>); @@ -344,7 +299,7 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> { next_early_index, track_lifetime_uses, opaque_type_parent, - from_poly_trait_ref, + scope_type, hir_id, s: _, } => f @@ -353,7 +308,7 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> { .field("next_early_index", next_early_index) .field("track_lifetime_uses", track_lifetime_uses) .field("opaque_type_parent", opaque_type_parent) - .field("from_poly_trait_ref", from_poly_trait_ref) + .field("scope_type", scope_type) .field("hir_id", hir_id) .field("s", &"..") .finish(), @@ -368,11 +323,6 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> { .field("lifetime", lifetime) .field("s", &"..") .finish(), - Scope::TraitRefHackInner { hir_id, s: _ } => f - .debug_struct("TraitRefHackInner") - .field("hir_id", hir_id) - .field("s", &"..") - .finish(), Scope::Supertrait { lifetimes, s: _ } => f .debug_struct("Supertrait") .field("lifetimes", lifetimes) @@ -495,7 +445,6 @@ fn do_resolve( tcx, map: &mut named_region_map, scope: ROOT_SCOPE, - trait_ref_hack: None, is_in_fn_syntax: false, is_in_const_generic: false, trait_definition_only, @@ -618,6 +567,43 @@ fn late_region_as_bound_region<'tcx>(tcx: TyCtxt<'tcx>, region: &Region) -> ty:: } } +impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { + /// Returns the binders in scope and the type of `Binder` that should be created for a poly trait ref. + fn poly_trait_ref_binder_info(&mut self) -> (Vec, BinderScopeType) { + let mut scope = self.scope; + let mut supertrait_lifetimes = vec![]; + loop { + match scope { + Scope::Body { .. } | Scope::Root => { + break (vec![], BinderScopeType::Normal); + } + + Scope::Elision { s, .. } | Scope::ObjectLifetimeDefault { s, .. } => { + scope = s; + } + + Scope::Supertrait { s, lifetimes } => { + supertrait_lifetimes = lifetimes.clone(); + scope = s; + } + + Scope::TraitRefBoundary { .. } => { + // We should only see super trait lifetimes if there is a `Binder` above + assert!(supertrait_lifetimes.is_empty()); + break (vec![], BinderScopeType::Normal); + } + + Scope::Binder { hir_id, .. } => { + // Nested poly trait refs have the binders concatenated + let mut full_binders = + self.map.late_bound_vars.entry(*hir_id).or_default().clone(); + full_binders.extend(supertrait_lifetimes.into_iter()); + break (full_binders, BinderScopeType::Concatenating); + } + } + } + } +} impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { type Map = Map<'tcx>; @@ -675,7 +661,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { s: self.scope, track_lifetime_uses: true, opaque_type_parent: false, - from_poly_trait_ref: false, + scope_type: BinderScopeType::Normal, }; self.with(scope, move |_old_scope, this| { intravisit::walk_fn(this, fk, fd, b, s, hir_id) @@ -800,12 +786,15 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { next_early_index: index + non_lifetime_count, opaque_type_parent: true, track_lifetime_uses, - from_poly_trait_ref: false, + scope_type: BinderScopeType::Normal, s: ROOT_SCOPE, }; self.with(scope, |old_scope, this| { this.check_lifetime_params(old_scope, &generics.params); - intravisit::walk_item(this, item); + let scope = Scope::TraitRefBoundary { s: this.scope }; + this.with(scope, |_, this| { + intravisit::walk_item(this, item); + }); }); self.missing_named_lifetime_spots.pop(); } @@ -869,7 +858,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { next_early_index, track_lifetime_uses: true, opaque_type_parent: false, - from_poly_trait_ref: false, + scope_type: BinderScopeType::Normal, }; self.with(scope, |old_scope, this| { // a bare fn has no bounds, so everything @@ -939,9 +928,12 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { // Elided lifetimes are not allowed in non-return // position impl Trait - let scope = Scope::Elision { elide: Elide::Forbid, s: self.scope }; + let scope = Scope::TraitRefBoundary { s: self.scope }; self.with(scope, |_, this| { - intravisit::walk_item(this, opaque_ty); + let scope = Scope::Elision { elide: Elide::Forbid, s: this.scope }; + this.with(scope, |_, this| { + intravisit::walk_item(this, opaque_ty); + }) }); return; @@ -1062,7 +1054,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { s: this.scope, track_lifetime_uses: true, opaque_type_parent: false, - from_poly_trait_ref: false, + scope_type: BinderScopeType::Normal, }; this.with(scope, |_old_scope, this| { this.visit_generics(generics); @@ -1082,7 +1074,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { s: self.scope, track_lifetime_uses: true, opaque_type_parent: false, - from_poly_trait_ref: false, + scope_type: BinderScopeType::Normal, }; self.with(scope, |_old_scope, this| { let scope = Scope::TraitRefBoundary { s: this.scope }; @@ -1141,7 +1133,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { s: self.scope, track_lifetime_uses: true, opaque_type_parent: true, - from_poly_trait_ref: false, + scope_type: BinderScopeType::Normal, }; self.with(scope, |old_scope, this| { this.check_lifetime_params(old_scope, &generics.params); @@ -1210,7 +1202,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { s: self.scope, track_lifetime_uses: true, opaque_type_parent: true, - from_poly_trait_ref: false, + scope_type: BinderScopeType::Normal, }; self.with(scope, |old_scope, this| { this.check_lifetime_params(old_scope, &generics.params); @@ -1270,98 +1262,102 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { if !self.trait_definition_only { check_mixed_explicit_and_in_band_defs(self.tcx, &generics.params); } - for param in generics.params { - match param.kind { - GenericParamKind::Lifetime { .. } => {} - GenericParamKind::Type { ref default, .. } => { - walk_list!(self, visit_param_bound, param.bounds); - if let Some(ref ty) = default { - self.visit_ty(&ty); + let scope = Scope::TraitRefBoundary { s: self.scope }; + self.with(scope, |_, this| { + for param in generics.params { + match param.kind { + GenericParamKind::Lifetime { .. } => {} + GenericParamKind::Type { ref default, .. } => { + walk_list!(this, visit_param_bound, param.bounds); + if let Some(ref ty) = default { + this.visit_ty(&ty); + } + } + GenericParamKind::Const { ref ty, .. } => { + let was_in_const_generic = this.is_in_const_generic; + this.is_in_const_generic = true; + walk_list!(this, visit_param_bound, param.bounds); + this.visit_ty(&ty); + this.is_in_const_generic = was_in_const_generic; } } - GenericParamKind::Const { ref ty, .. } => { - let was_in_const_generic = self.is_in_const_generic; - self.is_in_const_generic = true; - walk_list!(self, visit_param_bound, param.bounds); - self.visit_ty(&ty); - self.is_in_const_generic = was_in_const_generic; - } - } - } - for predicate in generics.where_clause.predicates { - match predicate { - &hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate { - ref bounded_ty, - bounds, - ref bound_generic_params, - .. - }) => { - let (lifetimes, binders): (FxHashMap, Vec<_>) = - bound_generic_params - .iter() - .filter_map(|param| match param.kind { - GenericParamKind::Lifetime { .. } => Some(param), - _ => None, - }) - .enumerate() - .map(|(late_bound_idx, param)| { - let pair = - Region::late(late_bound_idx as u32, &self.tcx.hir(), param); - let r = late_region_as_bound_region(self.tcx, &pair.1); - (pair, r) - }) - .unzip(); - self.map.late_bound_vars.insert(bounded_ty.hir_id, binders.clone()); - let scope = Scope::TraitRefBoundary { s: self.scope }; - self.with(scope, |_, this| { - if !lifetimes.is_empty() { - let next_early_index = this.next_early_index(); - let scope = Scope::Binder { - hir_id: bounded_ty.hir_id, - lifetimes, - s: this.scope, - next_early_index, - track_lifetime_uses: true, - opaque_type_parent: false, - from_poly_trait_ref: true, - }; - this.with(scope, |old_scope, this| { - this.check_lifetime_params(old_scope, &bound_generic_params); - this.visit_ty(&bounded_ty); - this.trait_ref_hack = Some(bounded_ty.hir_id); - walk_list!(this, visit_param_bound, bounds); - this.trait_ref_hack = None; - }) - } else { + } + for predicate in generics.where_clause.predicates { + match predicate { + &hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate { + ref bounded_ty, + bounds, + ref bound_generic_params, + .. + }) => { + let (lifetimes, binders): (FxHashMap, Vec<_>) = + bound_generic_params + .iter() + .filter_map(|param| match param.kind { + GenericParamKind::Lifetime { .. } => Some(param), + _ => None, + }) + .enumerate() + .map(|(late_bound_idx, param)| { + let pair = + Region::late(late_bound_idx as u32, &this.tcx.hir(), param); + let r = late_region_as_bound_region(this.tcx, &pair.1); + (pair, r) + }) + .unzip(); + this.map.late_bound_vars.insert(bounded_ty.hir_id, binders.clone()); + let next_early_index = this.next_early_index(); + // Even if there are no lifetimes defined here, we still wrap it in a binder + // scope. If there happens to be a nested poly trait ref (an error), that + // will be `Concatenating` anyways, so we don't have to worry about the depth + // being wrong. + let scope = Scope::Binder { + hir_id: bounded_ty.hir_id, + lifetimes, + s: this.scope, + next_early_index, + track_lifetime_uses: true, + opaque_type_parent: false, + scope_type: BinderScopeType::Normal, + }; + this.with(scope, |old_scope, this| { + this.check_lifetime_params(old_scope, &bound_generic_params); this.visit_ty(&bounded_ty); walk_list!(this, visit_param_bound, bounds); - } - }) - } - &hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate { - ref lifetime, - bounds, - .. - }) => { - self.visit_lifetime(lifetime); - walk_list!(self, visit_param_bound, bounds); - } - &hir::WherePredicate::EqPredicate(hir::WhereEqPredicate { - ref lhs_ty, - ref rhs_ty, - .. - }) => { - self.visit_ty(lhs_ty); - self.visit_ty(rhs_ty); + }) + } + &hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate { + ref lifetime, + bounds, + .. + }) => { + this.visit_lifetime(lifetime); + walk_list!(this, visit_param_bound, bounds); + } + &hir::WherePredicate::EqPredicate(hir::WhereEqPredicate { + ref lhs_ty, + ref rhs_ty, + .. + }) => { + this.visit_ty(lhs_ty); + this.visit_ty(rhs_ty); + } } } - } + }) } fn visit_param_bound(&mut self, bound: &'tcx hir::GenericBound<'tcx>) { match bound { - hir::GenericBound::LangItemTrait(_, _, hir_id, _) if self.trait_ref_hack.is_none() => { - self.map.late_bound_vars.insert(*hir_id, vec![]); + hir::GenericBound::LangItemTrait(_, _, hir_id, _) => { + // FIXME(jackh726): This is pretty weird. `LangItemTrait` doesn't go + // through the regular poly trait ref code, so we don't get another + // chance to introduce a binder. For now, I'm keeping the existing logic + // of "if there isn't a Binder scope above us, add one", but I + // imagine there's a better way to go about this. + let (binders, scope_type) = self.poly_trait_ref_binder_info(); + + self.map.late_bound_vars.insert(*hir_id, binders); let scope = Scope::Binder { hir_id: *hir_id, lifetimes: FxHashMap::default(), @@ -1369,7 +1365,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { next_early_index: self.next_early_index(), track_lifetime_uses: true, opaque_type_parent: false, - from_poly_trait_ref: false, + scope_type, }; self.with(scope, |_, this| { intravisit::walk_param_bound(this, bound); @@ -1388,148 +1384,53 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { let should_pop_missing_lt = self.is_trait_ref_fn_scope(trait_ref); - let trait_ref_hack = self.trait_ref_hack.take(); let next_early_index = self.next_early_index(); - // See note on `trait_ref_hack`. If `for<..>` has been defined in both - // the outer and inner part of the trait ref, emit an error. - let has_lifetimes = trait_ref.bound_generic_params.iter().any(|param| match param.kind { - GenericParamKind::Lifetime { .. } => true, - _ => false, - }); - if trait_ref_hack.is_some() && has_lifetimes { - struct_span_err!( - self.tcx.sess, - trait_ref.span, - E0316, - "nested quantification of lifetimes" - ) - .emit(); - } - - let (binders, lifetimes) = if let Some(hir_id) = trait_ref_hack { - let mut binders = self.map.late_bound_vars.entry(hir_id).or_default().clone(); - let initial_bound_vars = binders.len() as u32; - let mut lifetimes: FxHashMap = FxHashMap::default(); - let binders_iter = trait_ref - .bound_generic_params - .iter() - .filter_map(|param| match param.kind { - GenericParamKind::Lifetime { .. } => Some(param), - _ => None, - }) - .enumerate() - .map(|(late_bound_idx, param)| { - let pair = Region::late( - initial_bound_vars + late_bound_idx as u32, - &self.tcx.hir(), - param, - ); - let r = late_region_as_bound_region(self.tcx, &pair.1); - lifetimes.insert(pair.0, pair.1); - r - }); - binders.extend(binders_iter); - - (binders, lifetimes) - } else { - let mut supertrait_lifetimes = vec![]; - let mut scope = self.scope; - let mut outer_binders = loop { - match scope { - Scope::Body { .. } | Scope::Root => { - break vec![]; - } - - Scope::Elision { s, .. } | Scope::ObjectLifetimeDefault { s, .. } => { - scope = s; - } - - Scope::TraitRefHackInner { hir_id, .. } => { - // Nested poly trait refs have the binders concatenated - // If we reach `TraitRefHackInner`, then there is only one more `Binder` above us, - // over all the bounds. We don't want this, since all the lifetimes we care about - // are here anyways. - let mut full_binders = - self.map.late_bound_vars.entry(*hir_id).or_default().clone(); - full_binders.extend(supertrait_lifetimes.into_iter()); - break full_binders; - } - - Scope::Supertrait { s, lifetimes } => { - supertrait_lifetimes = lifetimes.clone(); - scope = s; - } - - Scope::TraitRefBoundary { .. } => { - // We should only see super trait lifetimes if there is a `Binder` above - assert!(supertrait_lifetimes.is_empty()); - break vec![]; - } - - Scope::Binder { hir_id, from_poly_trait_ref, .. } => { - if !from_poly_trait_ref { - // We should only see super trait lifetimes if there is a `Binder` above - assert!(supertrait_lifetimes.is_empty()); - break vec![]; - } - // Nested poly trait refs have the binders concatenated - let mut full_binders = - self.map.late_bound_vars.entry(*hir_id).or_default().clone(); - full_binders.extend(supertrait_lifetimes.into_iter()); - break full_binders; - } - } - }; - let (lifetimes, local_binders): (FxHashMap, Vec<_>) = trait_ref - .bound_generic_params - .iter() - .filter_map(|param| match param.kind { - GenericParamKind::Lifetime { .. } => Some(param), - _ => None, - }) - .enumerate() - .map(|(late_bound_idx, param)| { - let pair = Region::late( - outer_binders.len() as u32 + late_bound_idx as u32, - &self.tcx.hir(), - param, - ); - let r = late_region_as_bound_region(self.tcx, &pair.1); - (pair, r) - }) - .unzip(); - - outer_binders.extend(local_binders.into_iter()); + let (mut binders, scope_type) = self.poly_trait_ref_binder_info(); - (outer_binders, lifetimes) - }; + let initial_bound_vars = binders.len() as u32; + let mut lifetimes: FxHashMap = FxHashMap::default(); + let binders_iter = trait_ref + .bound_generic_params + .iter() + .filter_map(|param| match param.kind { + GenericParamKind::Lifetime { .. } => Some(param), + _ => None, + }) + .enumerate() + .map(|(late_bound_idx, param)| { + let pair = Region::late( + initial_bound_vars + late_bound_idx as u32, + &self.tcx.hir(), + param, + ); + let r = late_region_as_bound_region(self.tcx, &pair.1); + lifetimes.insert(pair.0, pair.1); + r + }); + binders.extend(binders_iter); debug!(?binders); self.map.late_bound_vars.insert(trait_ref.trait_ref.hir_ref_id, binders); - if trait_ref_hack.is_none() || has_lifetimes { - let scope = Scope::Binder { - hir_id: trait_ref.trait_ref.hir_ref_id, - lifetimes, - s: self.scope, - next_early_index, - track_lifetime_uses: true, - opaque_type_parent: false, - from_poly_trait_ref: true, - }; - self.with(scope, |old_scope, this| { - this.check_lifetime_params(old_scope, &trait_ref.bound_generic_params); - walk_list!(this, visit_generic_param, trait_ref.bound_generic_params); - this.visit_trait_ref(&trait_ref.trait_ref); - }); - } else { - let scope = - Scope::TraitRefHackInner { hir_id: trait_ref.trait_ref.hir_ref_id, s: self.scope }; - self.with(scope, |_old_scope, this| { - this.visit_trait_ref(&trait_ref.trait_ref); - }); - } - self.trait_ref_hack = trait_ref_hack; + // Always introduce a scope here, even if this is in a where clause and + // we introduced the binders around the bounded Ty. In that case, we + // just reuse the concatenation functionality also present in nested trait + // refs. + let scope = Scope::Binder { + hir_id: trait_ref.trait_ref.hir_ref_id, + lifetimes, + s: self.scope, + next_early_index, + track_lifetime_uses: true, + opaque_type_parent: false, + scope_type, + }; + self.with(scope, |old_scope, this| { + this.check_lifetime_params(old_scope, &trait_ref.bound_generic_params); + walk_list!(this, visit_generic_param, trait_ref.bound_generic_params); + this.visit_trait_ref(&trait_ref.trait_ref); + }); + if should_pop_missing_lt { self.missing_named_lifetime_spots.pop(); } @@ -1680,7 +1581,6 @@ fn extract_labels(ctxt: &mut LifetimeContext<'_, '_>, body: &hir::Body<'_>) { Scope::Body { s, .. } | Scope::Elision { s, .. } | Scope::ObjectLifetimeDefault { s, .. } - | Scope::TraitRefHackInner { s, .. } | Scope::Supertrait { s, .. } | Scope::TraitRefBoundary { s, .. } => { scope = s; @@ -1871,12 +1771,10 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { let labels_in_fn = take(&mut self.labels_in_fn); let xcrate_object_lifetime_defaults = take(&mut self.xcrate_object_lifetime_defaults); let missing_named_lifetime_spots = take(&mut self.missing_named_lifetime_spots); - let trait_ref_hack = take(&mut self.trait_ref_hack); let mut this = LifetimeContext { tcx: *tcx, map, scope: &wrap_scope, - trait_ref_hack, is_in_fn_syntax: self.is_in_fn_syntax, is_in_const_generic: self.is_in_const_generic, trait_definition_only: self.trait_definition_only, @@ -1896,7 +1794,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { self.labels_in_fn = this.labels_in_fn; self.xcrate_object_lifetime_defaults = this.xcrate_object_lifetime_defaults; self.missing_named_lifetime_spots = this.missing_named_lifetime_spots; - self.trait_ref_hack = this.trait_ref_hack; } /// helper method to determine the span to remove when suggesting the @@ -2265,7 +2162,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { s: self.scope, opaque_type_parent: true, track_lifetime_uses: false, - from_poly_trait_ref: false, + scope_type: BinderScopeType::Normal, }; self.with(scope, move |old_scope, this| { this.check_lifetime_params(old_scope, &generics.params); @@ -2289,7 +2186,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { | Scope::Body { s, .. } | Scope::Elision { s, .. } | Scope::ObjectLifetimeDefault { s, .. } - | Scope::TraitRefHackInner { s, .. } | Scope::Supertrait { s, .. } | Scope::TraitRefBoundary { s, .. } => scope = s, } @@ -2323,7 +2219,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { // given name or we run out of scopes. // search. let mut late_depth = 0; - let mut in_poly_trait_ref = false; let mut scope = self.scope; let mut outermost_body = None; let result = loop { @@ -2341,25 +2236,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { break None; } - Scope::TraitRefBoundary { s, .. } => { - // We've exited nested poly trait refs; mark that we are no longer in nested trait refs. - // We don't increase the late depth because this isn't a `Binder` scope. - // - // This came up in #83737, which boiled down to a case like this: - // - // ``` - // F: for<> Fn(&()) -> Box Future + Unpin>, - // // ^^^^^ - - // ``` - // - // Here, as we traverse upwards from the `dyn for<>` binder, we want to reset `in_poly_trait_ref` - // to false, so that we avoid excess contaenation when we encounter the outer `for<>` binder. - in_poly_trait_ref = false; - scope = s; - } - - Scope::Binder { ref lifetimes, from_poly_trait_ref, s, .. } => { + Scope::Binder { ref lifetimes, scope_type, s, .. } => { match lifetime_ref.name { LifetimeName::Param(param_name) => { if let Some(&def) = lifetimes.get(¶m_name.normalize_to_macros_2_0()) @@ -2369,47 +2246,17 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { } _ => bug!("expected LifetimeName::Param"), } - - match (from_poly_trait_ref, in_poly_trait_ref) { - // This is the first binder we see that is a poly trait ref; add one to the - // late depth and mark that we're potentially in nested trait refs. - (true, false) => { - in_poly_trait_ref = true; - late_depth += 1; - } - // We've already seen a binder that is a poly trait ref and this one is too, - // that means that they are nested and we are concatenating the bound vars; - // don't increase the late depth. - // - // This happens specifically with associated trait bounds like the following: - // - // ``` - // for<'a> T: Iterator Foo<'a, 'b>> - // ``` - // - // In this case, as we traverse `for<'b>`, we would increment `late_depth` but - // set `in_poly_trait_ref` to true. Then when we traverse `for<'a>`, we would - // not increment `late_depth` again. (NB: Niko thinks this logic is actually - // wrong.) - (true, true) => {} - // We've exited nested poly trait refs; add one to the late depth and mark - // that we are no longer in nested trait refs - (false, true) => { - in_poly_trait_ref = false; - late_depth += 1; - } - // Any other kind of nested binders: just increase late depth. - (false, false) => { - late_depth += 1; - } + match scope_type { + BinderScopeType::Normal => late_depth += 1, + BinderScopeType::Concatenating => {} } scope = s; } Scope::Elision { s, .. } | Scope::ObjectLifetimeDefault { s, .. } - | Scope::TraitRefHackInner { s, .. } - | Scope::Supertrait { s, .. } => { + | Scope::Supertrait { s, .. } + | Scope::TraitRefBoundary { s, .. } => { scope = s; } } @@ -2562,7 +2409,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { Scope::Binder { s, .. } | Scope::Elision { s, .. } | Scope::ObjectLifetimeDefault { s, .. } - | Scope::TraitRefHackInner { s, .. } | Scope::Supertrait { s, .. } | Scope::TraitRefBoundary { s, .. } => { scope = s; @@ -2761,7 +2607,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { let mut scope = &*self.scope; let hir_id = loop { match scope { - Scope::Binder { hir_id, .. } | Scope::TraitRefHackInner { hir_id, .. } => { + Scope::Binder { hir_id, .. } => { break *hir_id; } Scope::Body { id, .. } => break id.hir_id, @@ -3112,7 +2958,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { let span = lifetime_refs[0].span; let mut late_depth = 0; - let mut in_poly_trait_ref = false; let mut scope = self.scope; let mut lifetime_names = FxHashSet::default(); let mut lifetime_spans = vec![]; @@ -3123,14 +2968,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { Scope::Root => break None, - Scope::TraitRefBoundary { s, .. } => { - // We've exited nested poly trait refs; mark that we are no longer in nested trait refs. - // We don't increase the late depth because this isn't a `Binder` scope - in_poly_trait_ref = false; - scope = s; - } - - Scope::Binder { s, ref lifetimes, from_poly_trait_ref, .. } => { + Scope::Binder { s, ref lifetimes, scope_type, .. } => { // collect named lifetimes for suggestions for name in lifetimes.keys() { if let hir::ParamName::Plain(name) = name { @@ -3138,20 +2976,9 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { lifetime_spans.push(name.span); } } - // See comments in `resolve_lifetime_ref` - match (from_poly_trait_ref, in_poly_trait_ref) { - (true, false) => { - in_poly_trait_ref = true; - late_depth += 1; - } - (true, true) => {} - (false, true) => { - in_poly_trait_ref = false; - late_depth += 1; - } - (false, false) => { - late_depth += 1; - } + match scope_type { + BinderScopeType::Normal => late_depth += 1, + BinderScopeType::Concatenating => {} } scope = s; } @@ -3201,8 +3028,8 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { } Scope::ObjectLifetimeDefault { s, .. } - | Scope::TraitRefHackInner { s, .. } - | Scope::Supertrait { s, .. } => { + | Scope::Supertrait { s, .. } + | Scope::TraitRefBoundary { s, .. } => { scope = s; } } @@ -3308,31 +3135,13 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { fn resolve_object_lifetime_default(&mut self, lifetime_ref: &'tcx hir::Lifetime) { debug!("resolve_object_lifetime_default(lifetime_ref={:?})", lifetime_ref); let mut late_depth = 0; - let mut in_poly_trait_ref = false; let mut scope = self.scope; let lifetime = loop { match *scope { - Scope::TraitRefBoundary { s, .. } => { - // We've exited nested poly trait refs; mark that we are no longer in nested trait refs. - // We don't increase the late depth because this isn't a `Binder` scope - in_poly_trait_ref = false; - scope = s; - } - - Scope::Binder { s, from_poly_trait_ref, .. } => { - match (from_poly_trait_ref, in_poly_trait_ref) { - (true, false) => { - in_poly_trait_ref = true; - late_depth += 1; - } - (true, true) => {} - (false, true) => { - in_poly_trait_ref = false; - late_depth += 1; - } - (false, false) => { - late_depth += 1; - } + Scope::Binder { s, scope_type, .. } => { + match scope_type { + BinderScopeType::Normal => late_depth += 1, + BinderScopeType::Concatenating => {} } scope = s; } @@ -3343,7 +3152,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { Scope::ObjectLifetimeDefault { lifetime: Some(l), .. } => break l, - Scope::TraitRefHackInner { s, .. } | Scope::Supertrait { s, .. } => { + Scope::Supertrait { s, .. } | Scope::TraitRefBoundary { s, .. } => { scope = s; } } @@ -3470,7 +3279,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { Scope::Body { s, .. } | Scope::Elision { s, .. } | Scope::ObjectLifetimeDefault { s, .. } - | Scope::TraitRefHackInner { s, .. } | Scope::Supertrait { s, .. } | Scope::TraitRefBoundary { s, .. } => { old_scope = s; @@ -3529,7 +3337,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { } => break false, Scope::ObjectLifetimeDefault { s, .. } - | Scope::TraitRefHackInner { s, .. } | Scope::Supertrait { s, .. } | Scope::TraitRefBoundary { s, .. } => scope = s, } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 129954381c9ba..1d1969f7e78ab 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -239,8 +239,6 @@ enum ResolutionError<'a> { ForwardDeclaredTyParam, // FIXME(const_generics_defaults) /// ERROR E0770: the type of const parameters must not depend on other generic parameters. ParamInTyOfConstParam(Symbol), - /// constant values inside of type parameter defaults must not depend on generic parameters. - ParamInAnonConstInTyDefault(Symbol), /// generic parameters must not be used inside const evaluations. /// /// This error is only emitted when using `min_const_generics`. @@ -2672,26 +2670,18 @@ impl<'a> Resolver<'a> { } } Res::Def(DefKind::TyParam, _) | Res::SelfTy(..) => { - let mut in_ty_param_default = false; for rib in ribs { - let has_generic_params = match rib.kind { + let has_generic_params: HasGenericParams = match rib.kind { NormalRibKind | ClosureOrAsyncRibKind | AssocItemRibKind | ModuleRibKind(..) - | MacroDefinition(..) => { + | MacroDefinition(..) + | ForwardGenericParamBanRibKind => { // Nothing to do. Continue. continue; } - // We only forbid constant items if we are inside of type defaults, - // for example `struct Foo()]>` - ForwardGenericParamBanRibKind => { - // FIXME(const_generic_defaults): we may need to distinguish between - // being in type parameter defaults and const parameter defaults - in_ty_param_default = true; - continue; - } ConstantItemRibKind(trivial, _) => { let features = self.session.features_untracked(); // HACK(min_const_generics): We currently only allow `N` or `{ N }`. @@ -2720,19 +2710,7 @@ impl<'a> Resolver<'a> { } } - if in_ty_param_default { - if record_used { - self.report_error( - span, - ResolutionError::ParamInAnonConstInTyDefault( - rib_ident.name, - ), - ); - } - return Res::Err; - } else { - continue; - } + continue; } // This was an attempt to use a type parameter outside its scope. @@ -2770,23 +2748,15 @@ impl<'a> Resolver<'a> { ribs.next(); } - let mut in_ty_param_default = false; for rib in ribs { let has_generic_params = match rib.kind { NormalRibKind | ClosureOrAsyncRibKind | AssocItemRibKind | ModuleRibKind(..) - | MacroDefinition(..) => continue, - - // We only forbid constant items if we are inside of type defaults, - // for example `struct Foo()]>` - ForwardGenericParamBanRibKind => { - // FIXME(const_generic_defaults): we may need to distinguish between - // being in type parameter defaults and const parameter defaults - in_ty_param_default = true; - continue; - } + | MacroDefinition(..) + | ForwardGenericParamBanRibKind => continue, + ConstantItemRibKind(trivial, _) => { let features = self.session.features_untracked(); // HACK(min_const_generics): We currently only allow `N` or `{ N }`. @@ -2808,19 +2778,7 @@ impl<'a> Resolver<'a> { return Res::Err; } - if in_ty_param_default { - if record_used { - self.report_error( - span, - ResolutionError::ParamInAnonConstInTyDefault( - rib_ident.name, - ), - ); - } - return Res::Err; - } else { - continue; - } + continue; } ItemRibKind(has_generic_params) => has_generic_params, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 52270f0e6277b..1d1471fdeca04 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -796,7 +796,6 @@ symbols! { non_modrs_mods, none_error, nontemporal_store, - nontrapping_dash_fptoint: "nontrapping-fptoint", noop_method_borrow, noop_method_clone, noop_method_deref, diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index b5a458db6075f..ac5ec24eeeeb5 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -439,8 +439,7 @@ fn virtual_call_violation_for_method<'tcx>( return Some(MethodViolationCode::WhereClauseReferencesSelf); } - let receiver_ty = - tcx.liberate_late_bound_regions(method.def_id, sig.map_bound(|sig| sig.inputs()[0])); + let receiver_ty = tcx.liberate_late_bound_regions(method.def_id, sig.input(0)); // Until `unsized_locals` is fully implemented, `self: Self` can't be dispatched on. // However, this is already considered object-safe. We allow it as a special case here. diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index 29f1761b84d2b..38e5ce6fd831c 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -210,9 +210,9 @@ fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: DefId) -> &[DefId] { } } -fn associated_items(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AssociatedItems<'_> { +fn associated_items(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AssocItems<'_> { let items = tcx.associated_item_def_ids(def_id).iter().map(|did| tcx.associated_item(*did)); - ty::AssociatedItems::new(items) + ty::AssocItems::new(items) } fn def_ident_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option { diff --git a/compiler/rustc_typeck/src/astconv/generics.rs b/compiler/rustc_typeck/src/astconv/generics.rs index 7a297f2c65f13..2bbb38c294d57 100644 --- a/compiler/rustc_typeck/src/astconv/generics.rs +++ b/compiler/rustc_typeck/src/astconv/generics.rs @@ -286,7 +286,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ParamKindOrd::Const { unordered: tcx .features() - .const_generics, + .unordered_const_ty_params(), } } }, @@ -309,7 +309,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { GenericArg::Lifetime(_) => ParamKindOrd::Lifetime, GenericArg::Type(_) => ParamKindOrd::Type, GenericArg::Const(_) => ParamKindOrd::Const { - unordered: tcx.features().const_generics, + unordered: tcx + .features() + .unordered_const_ty_params(), }, }), Some(&format!( diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs index b6de491911ab7..62a1584d16be0 100644 --- a/compiler/rustc_typeck/src/astconv/mod.rs +++ b/compiler/rustc_typeck/src/astconv/mod.rs @@ -513,7 +513,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { GenericParamDefKind::Const { has_default } => { let ty = tcx.at(self.span).type_of(param.def_id); if !infer_args && has_default { - tcx.const_param_default(param.def_id).into() + tcx.const_param_default(param.def_id) + .subst_spanned(tcx, substs.unwrap(), Some(self.span)) + .into() } else { if infer_args { self.astconv.ct_infer(ty, Some(param), self.span).into() diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs index 9ace455042103..a50f8e1c65599 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs @@ -1446,7 +1446,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } GenericParamDefKind::Const { has_default, .. } => { if !infer_args && has_default { - tcx.const_param_default(param.def_id).into() + tcx.const_param_default(param.def_id) + .subst_spanned(tcx, substs.unwrap(), Some(self.span)) + .into() } else { self.fcx.var_for_def(self.span, param) } diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index 887cc42a1dd27..26871d6f0285c 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -728,20 +728,36 @@ fn check_where_clauses<'tcx, 'fcx>( // // Here, the default `Vec<[u32]>` is not WF because `[u32]: Sized` does not hold. for param in &generics.params { - if let GenericParamDefKind::Type { .. } = param.kind { - if is_our_default(¶m) { - let ty = fcx.tcx.type_of(param.def_id); - // Ignore dependent defaults -- that is, where the default of one type - // parameter includes another (e.g., ``). In those cases, we can't - // be sure if it will error or not as user might always specify the other. - if !ty.needs_subst() { + match param.kind { + GenericParamDefKind::Type { .. } => { + if is_our_default(¶m) { + let ty = fcx.tcx.type_of(param.def_id); + // Ignore dependent defaults -- that is, where the default of one type + // parameter includes another (e.g., ``). In those cases, we can't + // be sure if it will error or not as user might always specify the other. + if !ty.needs_subst() { + fcx.register_wf_obligation( + ty.into(), + fcx.tcx.def_span(param.def_id), + ObligationCauseCode::MiscObligation, + ); + } + } + } + GenericParamDefKind::Const { .. } => { + // FIXME(const_generics_defaults): Figure out if this + // is the behavior we want, see the comment further below. + if is_our_default(¶m) { + let default_ct = tcx.const_param_default(param.def_id); fcx.register_wf_obligation( - ty.into(), + default_ct.into(), fcx.tcx.def_span(param.def_id), ObligationCauseCode::MiscObligation, ); } } + // Doesn't have defaults. + GenericParamDefKind::Lifetime => {} } } @@ -774,14 +790,25 @@ fn check_where_clauses<'tcx, 'fcx>( fcx.tcx.mk_param_from_def(param) } GenericParamDefKind::Const { .. } => { + // FIXME(const_generics_defaults): I(@lcnr) feel like always + // using the const parameter is the right choice here, even + // if it needs substs. + // + // Before stabilizing this we probably want to get some tests + // where this makes a difference and figure out what's the exact + // behavior we want here. + + // If the param has a default, ... if is_our_default(param) { let default_ct = tcx.const_param_default(param.def_id); - // Const params currently have to be concrete. - assert!(!default_ct.needs_subst()); - default_ct.into() - } else { - fcx.tcx.mk_param_from_def(param) + // ... and it's not a dependent default, ... + if !default_ct.needs_subst() { + // ... then substitute it with the default. + return default_ct.into(); + } } + + fcx.tcx.mk_param_from_def(param) } } }); diff --git a/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs b/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs index 2965409999202..c69389e7b432b 100644 --- a/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs +++ b/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs @@ -24,8 +24,8 @@ impl InherentOverlapChecker<'tcx> { /// namespace. fn impls_have_common_items( &self, - impl_items1: &ty::AssociatedItems<'_>, - impl_items2: &ty::AssociatedItems<'_>, + impl_items1: &ty::AssocItems<'_>, + impl_items2: &ty::AssocItems<'_>, ) -> bool { let mut impl_items1 = &impl_items1; let mut impl_items2 = &impl_items2; diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index 1477418d5d8cf..927d8c57191a7 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -1316,13 +1316,13 @@ fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option Visitor<'v> for AnonConstInParamListDetector { +impl<'v> Visitor<'v> for AnonConstInParamTyDetector { type Map = intravisit::ErasedMap<'v>; fn nested_visit_map(&mut self) -> NestedVisitorMap { @@ -1330,15 +1330,17 @@ impl<'v> Visitor<'v> for AnonConstInParamListDetector { } fn visit_generic_param(&mut self, p: &'v hir::GenericParam<'v>) { - let prev = self.in_param_list; - self.in_param_list = true; - intravisit::walk_generic_param(self, p); - self.in_param_list = prev; + if let GenericParamKind::Const { ref ty, default: _ } = p.kind { + let prev = self.in_param_ty; + self.in_param_ty = true; + self.visit_ty(ty); + self.in_param_ty = prev; + } } fn visit_anon_const(&mut self, c: &'v hir::AnonConst) { - if self.in_param_list && self.ct == c.hir_id { - self.found_anon_const_in_list = true; + if self.in_param_ty && self.ct == c.hir_id { + self.found_anon_const_in_param_ty = true; } else { intravisit::walk_anon_const(self, c) } @@ -1366,27 +1368,24 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics { let parent_id = tcx.hir().get_parent_item(hir_id); let parent_def_id = tcx.hir().local_def_id(parent_id); - let mut in_param_list = false; + let mut in_param_ty = false; for (_parent, node) in tcx.hir().parent_iter(hir_id) { if let Some(generics) = node.generics() { - let mut visitor = AnonConstInParamListDetector { - in_param_list: false, - found_anon_const_in_list: false, + let mut visitor = AnonConstInParamTyDetector { + in_param_ty: false, + found_anon_const_in_param_ty: false, ct: hir_id, }; visitor.visit_generics(generics); - in_param_list = visitor.found_anon_const_in_list; + in_param_ty = visitor.found_anon_const_in_param_ty; break; } } - if in_param_list { + if in_param_ty { // We do not allow generic parameters in anon consts if we are inside - // of a param list. - // - // This affects both default type bindings, e.g. `struct()]>(T, U)`, - // and the types of const parameters, e.g. `struct V();`. + // of a const parameter type, e.g. `struct Foo` is not allowed. None } else if tcx.lazy_normalization() { // HACK(eddyb) this provides the correct generics when diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index c92b37b14be4f..f722430354991 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -81,6 +81,8 @@ mod private_slice_index { impl Sealed for ops::RangeInclusive {} #[stable(feature = "slice_get_slice", since = "1.28.0")] impl Sealed for ops::RangeToInclusive {} + #[stable(feature = "slice_index_with_ops_bound_pair", since = "1.53.0")] + impl Sealed for (ops::Bound, ops::Bound) {} } /// A helper trait used for indexing operations. @@ -546,3 +548,113 @@ where ops::Range { start, end } } + +/// Convert pair of `ops::Bound`s into `ops::Range` without performing any bounds checking and (in debug) overflow checking +fn into_range_unchecked( + len: usize, + (start, end): (ops::Bound, ops::Bound), +) -> ops::Range { + use ops::Bound; + let start = match start { + Bound::Included(i) => i, + Bound::Excluded(i) => i + 1, + Bound::Unbounded => 0, + }; + let end = match end { + Bound::Included(i) => i + 1, + Bound::Excluded(i) => i, + Bound::Unbounded => len, + }; + start..end +} + +/// Convert pair of `ops::Bound`s into `ops::Range`. +/// Returns `None` on overflowing indices. +fn into_range( + len: usize, + (start, end): (ops::Bound, ops::Bound), +) -> Option> { + use ops::Bound; + let start = match start { + Bound::Included(start) => start, + Bound::Excluded(start) => start.checked_add(1)?, + Bound::Unbounded => 0, + }; + + let end = match end { + Bound::Included(end) => end.checked_add(1)?, + Bound::Excluded(end) => end, + Bound::Unbounded => len, + }; + + // Don't bother with checking `start < end` and `end <= len` + // since these checks are handled by `Range` impls + + Some(start..end) +} + +/// Convert pair of `ops::Bound`s into `ops::Range`. +/// Panics on overflowing indices. +fn into_slice_range( + len: usize, + (start, end): (ops::Bound, ops::Bound), +) -> ops::Range { + use ops::Bound; + let start = match start { + Bound::Included(start) => start, + Bound::Excluded(start) => { + start.checked_add(1).unwrap_or_else(|| slice_start_index_overflow_fail()) + } + Bound::Unbounded => 0, + }; + + let end = match end { + Bound::Included(end) => { + end.checked_add(1).unwrap_or_else(|| slice_end_index_overflow_fail()) + } + Bound::Excluded(end) => end, + Bound::Unbounded => len, + }; + + // Don't bother with checking `start < end` and `end <= len` + // since these checks are handled by `Range` impls + + start..end +} + +#[stable(feature = "slice_index_with_ops_bound_pair", since = "1.53.0")] +unsafe impl SliceIndex<[T]> for (ops::Bound, ops::Bound) { + type Output = [T]; + + #[inline] + fn get(self, slice: &[T]) -> Option<&Self::Output> { + into_range(slice.len(), self)?.get(slice) + } + + #[inline] + fn get_mut(self, slice: &mut [T]) -> Option<&mut Self::Output> { + into_range(slice.len(), self)?.get_mut(slice) + } + + #[inline] + unsafe fn get_unchecked(self, slice: *const [T]) -> *const Self::Output { + // SAFETY: the caller has to uphold the safety contract for `get_unchecked`. + unsafe { into_range_unchecked(slice.len(), self).get_unchecked(slice) } + } + + #[inline] + unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut Self::Output { + // SAFETY: the caller has to uphold the safety contract for `get_unchecked_mut`. + unsafe { into_range_unchecked(slice.len(), self).get_unchecked_mut(slice) } + } + + #[inline] + fn index(self, slice: &[T]) -> &Self::Output { + into_slice_range(slice.len(), self).index(slice) + } + + #[inline] + fn index_mut(self, slice: &mut [T]) -> &mut Self::Output { + into_slice_range(slice.len(), self).index_mut(slice) + } +} diff --git a/library/core/tests/slice.rs b/library/core/tests/slice.rs index 7e198631cc7eb..3a98cd9d2ee91 100644 --- a/library/core/tests/slice.rs +++ b/library/core/tests/slice.rs @@ -1280,6 +1280,9 @@ mod slice_index { } )*) => {$( mod $case_name { + #[allow(unused_imports)] + use core::ops::Bound; + #[test] fn pass() { let mut v = $data; @@ -1376,6 +1379,24 @@ mod slice_index { bad: data[7..=6]; message: "out of range"; } + + in mod boundpair_len { + data: [0, 1, 2, 3, 4, 5]; + + good: data[(Bound::Included(6), Bound::Unbounded)] == []; + good: data[(Bound::Unbounded, Bound::Included(5))] == [0, 1, 2, 3, 4, 5]; + good: data[(Bound::Unbounded, Bound::Excluded(6))] == [0, 1, 2, 3, 4, 5]; + good: data[(Bound::Included(0), Bound::Included(5))] == [0, 1, 2, 3, 4, 5]; + good: data[(Bound::Included(0), Bound::Excluded(6))] == [0, 1, 2, 3, 4, 5]; + good: data[(Bound::Included(2), Bound::Excluded(4))] == [2, 3]; + good: data[(Bound::Excluded(1), Bound::Included(4))] == [2, 3, 4]; + good: data[(Bound::Excluded(5), Bound::Excluded(6))] == []; + good: data[(Bound::Included(6), Bound::Excluded(6))] == []; + good: data[(Bound::Excluded(5), Bound::Included(5))] == []; + good: data[(Bound::Included(6), Bound::Included(5))] == []; + bad: data[(Bound::Unbounded, Bound::Included(6))]; + message: "out of range"; + } } panic_cases! { @@ -1416,6 +1437,14 @@ mod slice_index { bad: data[4..=2]; message: "but ends at"; } + + in mod boundpair_neg_width { + data: [0, 1, 2, 3, 4, 5]; + + good: data[(Bound::Included(4), Bound::Excluded(4))] == []; + bad: data[(Bound::Included(4), Bound::Excluded(3))]; + message: "but ends at"; + } } panic_cases! { @@ -1434,6 +1463,20 @@ mod slice_index { bad: data[..= usize::MAX]; message: "maximum usize"; } + + in mod boundpair_overflow_end { + data: [0; 1]; + + bad: data[(Bound::Unbounded, Bound::Included(usize::MAX))]; + message: "maximum usize"; + } + + in mod boundpair_overflow_start { + data: [0; 1]; + + bad: data[(Bound::Excluded(usize::MAX), Bound::Unbounded)]; + message: "maximum usize"; + } } // panic_cases! } diff --git a/library/std/src/sys/hermit/args.rs b/library/std/src/sys/hermit/args.rs index 7727293927282..17b27c50bb2d3 100644 --- a/library/std/src/sys/hermit/args.rs +++ b/library/std/src/sys/hermit/args.rs @@ -1,5 +1,4 @@ use crate::ffi::OsString; -use crate::marker::PhantomData; use crate::vec; /// One-time global initialization. @@ -19,7 +18,6 @@ pub fn args() -> Args { pub struct Args { iter: vec::IntoIter, - _dont_send_or_sync_me: PhantomData<*mut ()>, } impl Args { @@ -28,6 +26,9 @@ impl Args { } } +impl !Send for Args {} +impl !Sync for Args {} + impl Iterator for Args { type Item = OsString; fn next(&mut self) -> Option { @@ -53,7 +54,6 @@ impl DoubleEndedIterator for Args { mod imp { use super::Args; use crate::ffi::{CStr, OsString}; - use crate::marker::PhantomData; use crate::ptr; use crate::sys_common::os_str_bytes::*; @@ -76,7 +76,7 @@ mod imp { } pub fn args() -> Args { - Args { iter: clone().into_iter(), _dont_send_or_sync_me: PhantomData } + Args { iter: clone().into_iter() } } fn clone() -> Vec { diff --git a/library/std/src/sys/hermit/os.rs b/library/std/src/sys/hermit/os.rs index 4487e9d636cb0..2d60c4f98ee50 100644 --- a/library/std/src/sys/hermit/os.rs +++ b/library/std/src/sys/hermit/os.rs @@ -3,7 +3,6 @@ use crate::error::Error as StdError; use crate::ffi::{CStr, OsStr, OsString}; use crate::fmt; use crate::io; -use crate::marker::PhantomData; use crate::memchr; use crate::path::{self, PathBuf}; use crate::str; @@ -110,9 +109,11 @@ pub fn init_environment(env: *const *const i8) { pub struct Env { iter: vec::IntoIter<(OsString, OsString)>, - _dont_send_or_sync_me: PhantomData<*mut ()>, } +impl !Send for Env {} +impl !Sync for Env {} + impl Iterator for Env { type Item = (OsString, OsString); fn next(&mut self) -> Option<(OsString, OsString)> { @@ -134,7 +135,7 @@ pub fn env() -> Env { result.push((key.clone(), value.clone())); } - return Env { iter: result.into_iter(), _dont_send_or_sync_me: PhantomData }; + return Env { iter: result.into_iter() }; } } diff --git a/library/std/src/sys/unix/args.rs b/library/std/src/sys/unix/args.rs index 6967647249390..4fca0d0752fe8 100644 --- a/library/std/src/sys/unix/args.rs +++ b/library/std/src/sys/unix/args.rs @@ -6,7 +6,6 @@ #![allow(dead_code)] // runtime init functions not used during testing use crate::ffi::OsString; -use crate::marker::PhantomData; use crate::vec; /// One-time global initialization. @@ -26,9 +25,11 @@ pub fn args() -> Args { pub struct Args { iter: vec::IntoIter, - _dont_send_or_sync_me: PhantomData<*mut ()>, } +impl !Send for Args {} +impl !Sync for Args {} + impl Args { pub fn inner_debug(&self) -> &[OsString] { self.iter.as_slice() @@ -76,7 +77,6 @@ impl DoubleEndedIterator for Args { mod imp { use super::Args; use crate::ffi::{CStr, OsString}; - use crate::marker::PhantomData; use crate::os::unix::prelude::*; use crate::ptr; use crate::sync::atomic::{AtomicIsize, AtomicPtr, Ordering}; @@ -133,7 +133,7 @@ mod imp { } pub fn args() -> Args { - Args { iter: clone().into_iter(), _dont_send_or_sync_me: PhantomData } + Args { iter: clone().into_iter() } } fn clone() -> Vec { @@ -155,7 +155,6 @@ mod imp { mod imp { use super::Args; use crate::ffi::CStr; - use crate::marker::PhantomData; pub unsafe fn init(_argc: isize, _argv: *const *const u8) {} @@ -180,7 +179,7 @@ mod imp { }) .collect::>() }; - Args { iter: vec.into_iter(), _dont_send_or_sync_me: PhantomData } + Args { iter: vec.into_iter() } } // As _NSGetArgc and _NSGetArgv aren't mentioned in iOS docs @@ -247,6 +246,6 @@ mod imp { } } - Args { iter: res.into_iter(), _dont_send_or_sync_me: PhantomData } + Args { iter: res.into_iter() } } } diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs index bf649f6d76f81..984c08c2ad531 100644 --- a/library/std/src/sys/unix/os.rs +++ b/library/std/src/sys/unix/os.rs @@ -12,7 +12,6 @@ use crate::ffi::{CStr, CString, OsStr, OsString}; use crate::fmt; use crate::io; use crate::iter; -use crate::marker::PhantomData; use crate::mem; use crate::memchr; use crate::path::{self, PathBuf}; @@ -21,8 +20,8 @@ use crate::slice; use crate::str; use crate::sys::cvt; use crate::sys::fd; +use crate::sys::rwlock::{RWLockReadGuard, StaticRWLock}; use crate::sys_common::mutex::{StaticMutex, StaticMutexGuard}; -use crate::sys_common::rwlock::{RWLockReadGuard, StaticRWLock}; use crate::vec; use libc::{c_char, c_int, c_void}; @@ -460,9 +459,11 @@ pub fn current_exe() -> io::Result { pub struct Env { iter: vec::IntoIter<(OsString, OsString)>, - _dont_send_or_sync_me: PhantomData<*mut ()>, } +impl !Send for Env {} +impl !Sync for Env {} + impl Iterator for Env { type Item = (OsString, OsString); fn next(&mut self) -> Option<(OsString, OsString)> { @@ -510,7 +511,7 @@ pub fn env() -> Env { environ = environ.add(1); } } - return Env { iter: result.into_iter(), _dont_send_or_sync_me: PhantomData }; + return Env { iter: result.into_iter() }; } fn parse(input: &[u8]) -> Option<(OsString, OsString)> { diff --git a/library/std/src/sys/unix/rwlock.rs b/library/std/src/sys/unix/rwlock.rs index 2b5067a34f648..d97d9d712fc93 100644 --- a/library/std/src/sys/unix/rwlock.rs +++ b/library/std/src/sys/unix/rwlock.rs @@ -139,3 +139,55 @@ impl RWLock { } } } + +pub struct StaticRWLock(RWLock); + +impl StaticRWLock { + pub const fn new() -> StaticRWLock { + StaticRWLock(RWLock::new()) + } + + /// Acquires shared access to the underlying lock, blocking the current + /// thread to do so. + /// + /// The lock is automatically unlocked when the returned guard is dropped. + #[inline] + pub fn read_with_guard(&'static self) -> RWLockReadGuard { + // SAFETY: All methods require static references, therefore self + // cannot be moved between invocations. + unsafe { + self.0.read(); + } + RWLockReadGuard(&self.0) + } + + /// Acquires write access to the underlying lock, blocking the current thread + /// to do so. + /// + /// The lock is automatically unlocked when the returned guard is dropped. + #[inline] + pub fn write_with_guard(&'static self) -> RWLockWriteGuard { + // SAFETY: All methods require static references, therefore self + // cannot be moved between invocations. + unsafe { + self.0.write(); + } + RWLockWriteGuard(&self.0) + } +} + +pub struct RWLockReadGuard(&'static RWLock); + +impl Drop for RWLockReadGuard { + fn drop(&mut self) { + unsafe { self.0.read_unlock() } + } +} + +pub struct RWLockWriteGuard(&'static RWLock); + +impl Drop for RWLockWriteGuard { + fn drop(&mut self) { + unsafe { self.0.write_unlock() } + } +} diff --git a/library/std/src/sys/wasi/args.rs b/library/std/src/sys/wasi/args.rs index 9a27218e1fb70..61a9bd28fd812 100644 --- a/library/std/src/sys/wasi/args.rs +++ b/library/std/src/sys/wasi/args.rs @@ -1,7 +1,6 @@ #![deny(unsafe_op_in_unsafe_fn)] use crate::ffi::{CStr, OsStr, OsString}; -use crate::marker::PhantomData; use crate::os::wasi::ffi::OsStrExt; use crate::vec; @@ -11,15 +10,14 @@ pub unsafe fn cleanup() {} pub struct Args { iter: vec::IntoIter, - _dont_send_or_sync_me: PhantomData<*mut ()>, } +impl !Send for Args {} +impl !Sync for Args {} + /// Returns the command line arguments pub fn args() -> Args { - Args { - iter: maybe_args().unwrap_or(Vec::new()).into_iter(), - _dont_send_or_sync_me: PhantomData, - } + Args { iter: maybe_args().unwrap_or(Vec::new()).into_iter() } } fn maybe_args() -> Option> { diff --git a/library/std/src/sys/wasi/os.rs b/library/std/src/sys/wasi/os.rs index cf17ac0ba5f2b..78e97765693dc 100644 --- a/library/std/src/sys/wasi/os.rs +++ b/library/std/src/sys/wasi/os.rs @@ -5,7 +5,6 @@ use crate::error::Error as StdError; use crate::ffi::{CStr, CString, OsStr, OsString}; use crate::fmt; use crate::io; -use crate::marker::PhantomData; use crate::os::wasi::prelude::*; use crate::path::{self, PathBuf}; use crate::str; @@ -129,9 +128,11 @@ pub fn current_exe() -> io::Result { } pub struct Env { iter: vec::IntoIter<(OsString, OsString)>, - _dont_send_or_sync_me: PhantomData<*mut ()>, } +impl !Send for Env {} +impl !Sync for Env {} + impl Iterator for Env { type Item = (OsString, OsString); fn next(&mut self) -> Option<(OsString, OsString)> { @@ -155,7 +156,7 @@ pub fn env() -> Env { environ = environ.add(1); } } - return Env { iter: result.into_iter(), _dont_send_or_sync_me: PhantomData }; + return Env { iter: result.into_iter() }; } // See src/libstd/sys/unix/os.rs, same as that diff --git a/library/std/src/sys/wasm/args.rs b/library/std/src/sys/wasm/args.rs index 3b6557ae3257f..8c25a1d262a63 100644 --- a/library/std/src/sys/wasm/args.rs +++ b/library/std/src/sys/wasm/args.rs @@ -1,5 +1,4 @@ use crate::ffi::OsString; -use crate::marker::PhantomData; use crate::vec; pub unsafe fn init(_argc: isize, _argv: *const *const u8) { @@ -9,14 +8,16 @@ pub unsafe fn init(_argc: isize, _argv: *const *const u8) { pub unsafe fn cleanup() {} pub fn args() -> Args { - Args { iter: Vec::new().into_iter(), _dont_send_or_sync_me: PhantomData } + Args { iter: Vec::new().into_iter() } } pub struct Args { iter: vec::IntoIter, - _dont_send_or_sync_me: PhantomData<*mut ()>, } +impl !Send for Args {} +impl !Sync for Args {} + impl Args { pub fn inner_debug(&self) -> &[OsString] { self.iter.as_slice() diff --git a/library/std/src/sys_common/rwlock.rs b/library/std/src/sys_common/rwlock.rs index 70b31b19f824c..3705d641a1be6 100644 --- a/library/std/src/sys_common/rwlock.rs +++ b/library/std/src/sys_common/rwlock.rs @@ -86,62 +86,3 @@ impl RWLock { self.0.destroy() } } - -// the cfg annotations only exist due to dead code warnings. the code itself is portable -#[cfg(unix)] -pub struct StaticRWLock(RWLock); - -#[cfg(unix)] -impl StaticRWLock { - pub const fn new() -> StaticRWLock { - StaticRWLock(RWLock::new()) - } - - /// Acquires shared access to the underlying lock, blocking the current - /// thread to do so. - /// - /// The lock is automatically unlocked when the returned guard is dropped. - #[inline] - pub fn read_with_guard(&'static self) -> RWLockReadGuard { - // SAFETY: All methods require static references, therefore self - // cannot be moved between invocations. - unsafe { - self.0.read(); - } - RWLockReadGuard(&self.0) - } - - /// Acquires write access to the underlying lock, blocking the current thread - /// to do so. - /// - /// The lock is automatically unlocked when the returned guard is dropped. - #[inline] - pub fn write_with_guard(&'static self) -> RWLockWriteGuard { - // SAFETY: All methods require static references, therefore self - // cannot be moved between invocations. - unsafe { - self.0.write(); - } - RWLockWriteGuard(&self.0) - } -} - -#[cfg(unix)] -pub struct RWLockReadGuard(&'static RWLock); - -#[cfg(unix)] -impl Drop for RWLockReadGuard { - fn drop(&mut self) { - unsafe { self.0.read_unlock() } - } -} - -#[cfg(unix)] -pub struct RWLockWriteGuard(&'static RWLock); - -#[cfg(unix)] -impl Drop for RWLockWriteGuard { - fn drop(&mut self) { - unsafe { self.0.write_unlock() } - } -} diff --git a/src/librustdoc/html/static/rustdoc.css b/src/librustdoc/html/static/rustdoc.css index 8f11dda2fb205..213ca9ec9e3ea 100644 --- a/src/librustdoc/html/static/rustdoc.css +++ b/src/librustdoc/html/static/rustdoc.css @@ -116,6 +116,8 @@ h4:not(.method):not(.type):not(.tymethod):not(.associatedconstant) { padding-bottom: 6px; } h1.fqn { + display: flex; + width: 100%; border-bottom: 1px dashed; margin-top: 0; } @@ -458,6 +460,13 @@ nav.sub { font-weight: normal; } +h1.fqn > .out-of-band { + float: unset; + flex: 1; + text-align: right; + margin-left: 8px; +} + h3.impl > .out-of-band { font-size: 21px; } @@ -1450,10 +1459,6 @@ h4 > .notable-traits { padding: 0; } - .content .in-band { - width: 100%; - } - .content h4 > .out-of-band { position: inherit; } diff --git a/src/llvm-project b/src/llvm-project index ea6bb2615f04d..0ed6038a318e3 160000 --- a/src/llvm-project +++ b/src/llvm-project @@ -1 +1 @@ -Subproject commit ea6bb2615f04d53db11b6a43a14be5c9d1eaebe1 +Subproject commit 0ed6038a318e34e3d76a9e55bdebc4cfd17f902a diff --git a/src/test/ui/associated-consts/associated-const-dead-code.stderr b/src/test/ui/associated-consts/associated-const-dead-code.stderr index 9b6bbb68a71f7..e9915ba9e96a5 100644 --- a/src/test/ui/associated-consts/associated-const-dead-code.stderr +++ b/src/test/ui/associated-consts/associated-const-dead-code.stderr @@ -2,8 +2,11 @@ error: associated constant is never used: `BAR` --> $DIR/associated-const-dead-code.rs:6:5 | LL | const BAR: u32 = 1; - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^---^^^^^^^^^^ + | | + | help: if this is intentional, prefix it with an underscore: `_BAR` | + = note: the leading underscore signals that this associated constant serves some other purpose even if it isn't used in a way that we can detect. note: the lint level is defined here --> $DIR/associated-const-dead-code.rs:1:9 | diff --git a/src/test/ui/codemap_tests/two_files.stderr b/src/test/ui/codemap_tests/two_files.stderr index de2ffc2e5dc1d..aff51ee9e2f54 100644 --- a/src/test/ui/codemap_tests/two_files.stderr +++ b/src/test/ui/codemap_tests/two_files.stderr @@ -5,10 +5,9 @@ LL | impl Bar for Baz { } | ^^^ type aliases cannot be used as traits | help: you might have meant to use `#![feature(trait_alias)]` instead of a `type` alias - --> $DIR/two_files_data.rs:5:1 | -LL | type Bar = dyn Foo; - | ^^^^^^^^^^^^^^^^^^^ +LL | trait Bar = dyn Foo; + | error: aborting due to previous error diff --git a/src/test/ui/const-generics/defaults/auxiliary/const_defaulty.rs b/src/test/ui/const-generics/defaults/auxiliary/const_defaulty.rs index 769b6e952dc9c..6514409698e3e 100644 --- a/src/test/ui/const-generics/defaults/auxiliary/const_defaulty.rs +++ b/src/test/ui/const-generics/defaults/auxiliary/const_defaulty.rs @@ -1,4 +1,4 @@ -#![feature(const_generics)] +#![cfg_attr(full, feature(const_generics))] #![feature(const_generics_defaults)] #![allow(incomplete_features)] diff --git a/src/test/ui/const-generics/defaults/complex-generic-default-expr.full.stderr b/src/test/ui/const-generics/defaults/complex-generic-default-expr.full.stderr new file mode 100644 index 0000000000000..e0e2b6c69f280 --- /dev/null +++ b/src/test/ui/const-generics/defaults/complex-generic-default-expr.full.stderr @@ -0,0 +1,18 @@ +error: constant expression depends on a generic parameter + --> $DIR/complex-generic-default-expr.rs:6:34 + | +LL | struct Foo; + | ^ + | + = note: this may fail depending on what value the parameter takes + +error: constant expression depends on a generic parameter + --> $DIR/complex-generic-default-expr.rs:10:21 + | +LL | struct Bar() }>(T); + | ^^^^^^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/const-generics/defaults/complex-generic-default-expr.min.stderr b/src/test/ui/const-generics/defaults/complex-generic-default-expr.min.stderr new file mode 100644 index 0000000000000..58abd8db9f09f --- /dev/null +++ b/src/test/ui/const-generics/defaults/complex-generic-default-expr.min.stderr @@ -0,0 +1,20 @@ +error: generic parameters may not be used in const operations + --> $DIR/complex-generic-default-expr.rs:6:47 + | +LL | struct Foo; + | ^ cannot perform const operation using `N` + | + = help: const parameters may only be used as standalone arguments, i.e. `N` + = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions + +error: generic parameters may not be used in const operations + --> $DIR/complex-generic-default-expr.rs:10:62 + | +LL | struct Bar() }>(T); + | ^ cannot perform const operation using `T` + | + = note: type parameters may not be used in const expressions + = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/const-generics/defaults/complex-generic-default-expr.rs b/src/test/ui/const-generics/defaults/complex-generic-default-expr.rs new file mode 100644 index 0000000000000..a7b712f7b4b86 --- /dev/null +++ b/src/test/ui/const-generics/defaults/complex-generic-default-expr.rs @@ -0,0 +1,14 @@ +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![feature(const_generics_defaults)] +#![allow(incomplete_features)] + +struct Foo; +//[full]~^ ERROR constant expression depends on a generic parameter +//[min]~^^ ERROR generic parameters may not be used in const operations + +struct Bar() }>(T); +//[full]~^ ERROR constant expression depends on a generic parameter +//[min]~^^ ERROR generic parameters may not be used in const operations + +fn main() {} diff --git a/src/test/ui/const-generics/defaults/const-default.rs b/src/test/ui/const-generics/defaults/const-default.rs index 150c70770ae51..4fa21b8b1fb78 100644 --- a/src/test/ui/const-generics/defaults/const-default.rs +++ b/src/test/ui/const-generics/defaults/const-default.rs @@ -1,6 +1,6 @@ // run-pass - -#![feature(const_generics)] +// revisions: full min +#![cfg_attr(full, feature(const_generics))] #![feature(const_generics_defaults)] #![allow(incomplete_features)] diff --git a/src/test/ui/const-generics/defaults/const-param-as-default-value.rs b/src/test/ui/const-generics/defaults/const-param-as-default-value.rs new file mode 100644 index 0000000000000..59ac261f44fd5 --- /dev/null +++ b/src/test/ui/const-generics/defaults/const-param-as-default-value.rs @@ -0,0 +1,23 @@ +// run-pass +#![feature(const_generics_defaults)] +#![allow(incomplete_features)] +struct Foo([u8; N], [u8; M]); + +fn foo() -> Foo { + let x = [0; N]; + Foo(x, x) +} + +// To check that we actually apply the correct substs for const param defaults. +fn concrete_foo() -> Foo<13> { + Foo(Default::default(), Default::default()) +} + + +fn main() { + let val = foo::<13>(); + assert_eq!(val.0, val.1); + + let val = concrete_foo(); + assert_eq!(val.0, val.1); +} diff --git a/src/test/ui/const-generics/defaults/const-param-in-ty-defaults.rs b/src/test/ui/const-generics/defaults/const-param-in-ty-defaults.rs new file mode 100644 index 0000000000000..3f534ca0308ba --- /dev/null +++ b/src/test/ui/const-generics/defaults/const-param-in-ty-defaults.rs @@ -0,0 +1,14 @@ +// run-pass +#![feature(const_generics_defaults)] +#![allow(incomplete_features)] +struct Foo(T); + +impl Foo { + fn new() -> Self { + Foo([0; N]) + } +} + +fn main() { + assert_eq!(Foo::new().0, [0; 10]); +} diff --git a/src/test/ui/const-generics/defaults/default-on-impl.full.stderr b/src/test/ui/const-generics/defaults/default-on-impl.full.stderr new file mode 100644 index 0000000000000..c417a26842ed1 --- /dev/null +++ b/src/test/ui/const-generics/defaults/default-on-impl.full.stderr @@ -0,0 +1,8 @@ +error: defaults for const parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions + --> $DIR/default-on-impl.rs:8:12 + | +LL | impl Foo {} + | ^ + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/defaults/default-on-impl.min.stderr b/src/test/ui/const-generics/defaults/default-on-impl.min.stderr new file mode 100644 index 0000000000000..c417a26842ed1 --- /dev/null +++ b/src/test/ui/const-generics/defaults/default-on-impl.min.stderr @@ -0,0 +1,8 @@ +error: defaults for const parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions + --> $DIR/default-on-impl.rs:8:12 + | +LL | impl Foo {} + | ^ + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/defaults/default-on-impl.rs b/src/test/ui/const-generics/defaults/default-on-impl.rs new file mode 100644 index 0000000000000..735549defeaf0 --- /dev/null +++ b/src/test/ui/const-generics/defaults/default-on-impl.rs @@ -0,0 +1,11 @@ +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![feature(const_generics_defaults)] +#![allow(incomplete_features)] + +struct Foo; + +impl Foo {} +//~^ ERROR defaults for const parameters are only allowed + +fn main() {} diff --git a/src/test/ui/const-generics/defaults/default-param-wf-concrete.rs b/src/test/ui/const-generics/defaults/default-param-wf-concrete.rs new file mode 100644 index 0000000000000..4bb56c6a1c08c --- /dev/null +++ b/src/test/ui/const-generics/defaults/default-param-wf-concrete.rs @@ -0,0 +1,5 @@ +#![feature(const_generics_defaults)] +#![allow(incomplete_features)] +struct Foo; +//~^ ERROR evaluation of constant value failed +fn main() {} diff --git a/src/test/ui/const-generics/defaults/default-param-wf-concrete.stderr b/src/test/ui/const-generics/defaults/default-param-wf-concrete.stderr new file mode 100644 index 0000000000000..8464ea98bf695 --- /dev/null +++ b/src/test/ui/const-generics/defaults/default-param-wf-concrete.stderr @@ -0,0 +1,9 @@ +error[E0080]: evaluation of constant value failed + --> $DIR/default-param-wf-concrete.rs:3:28 + | +LL | struct Foo; + | ^^^^^^^ attempt to compute `u8::MAX + 1_u8`, which would overflow + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/const-generics/defaults/external.rs b/src/test/ui/const-generics/defaults/external.rs index b39e69ab10b66..32acf567cf2b9 100644 --- a/src/test/ui/const-generics/defaults/external.rs +++ b/src/test/ui/const-generics/defaults/external.rs @@ -1,5 +1,7 @@ // aux-build:const_defaulty.rs // check-pass +// revisions: full min +#![cfg_attr(full, feature(const_generics))] #![feature(const_generics_defaults)] #![allow(incomplete_features)] diff --git a/src/test/ui/const-generics/defaults/intermixed-lifetime.full.stderr b/src/test/ui/const-generics/defaults/intermixed-lifetime.full.stderr index c4a666a829d8c..29d835e36c6eb 100644 --- a/src/test/ui/const-generics/defaults/intermixed-lifetime.full.stderr +++ b/src/test/ui/const-generics/defaults/intermixed-lifetime.full.stderr @@ -1,5 +1,5 @@ error: lifetime parameters must be declared prior to const parameters - --> $DIR/intermixed-lifetime.rs:6:28 + --> $DIR/intermixed-lifetime.rs:7:28 | LL | struct Foo(&'a (), T); | -----------------^^---------- help: reorder the parameters: lifetimes, then consts and types: `<'a, const N: usize, T = u32>` diff --git a/src/test/ui/const-generics/defaults/intermixed-lifetime.min.stderr b/src/test/ui/const-generics/defaults/intermixed-lifetime.min.stderr index 69a490978d1df..985e7b655ece9 100644 --- a/src/test/ui/const-generics/defaults/intermixed-lifetime.min.stderr +++ b/src/test/ui/const-generics/defaults/intermixed-lifetime.min.stderr @@ -1,26 +1,14 @@ error: lifetime parameters must be declared prior to const parameters - --> $DIR/intermixed-lifetime.rs:6:28 + --> $DIR/intermixed-lifetime.rs:7:28 | LL | struct Foo(&'a (), T); - | -----------------^^---------- help: reorder the parameters: lifetimes, then types, then consts: `<'a, T = u32, const N: usize>` + | -----------------^^---------- help: reorder the parameters: lifetimes, then types, then consts: `<'a, const N: usize, T = u32>` -error: type parameters must be declared prior to const parameters - --> $DIR/intermixed-lifetime.rs:6:32 - | -LL | struct Foo(&'a (), T); - | ---------------------^------- help: reorder the parameters: lifetimes, then types, then consts: `<'a, T = u32, const N: usize>` - -error: lifetime parameters must be declared prior to const parameters +error: lifetime parameters must be declared prior to type parameters --> $DIR/intermixed-lifetime.rs:10:37 | LL | struct Bar(&'a (), T); - | --------------------------^^- help: reorder the parameters: lifetimes, then types, then consts: `<'a, T = u32, const N: usize>` - -error: type parameters must be declared prior to const parameters - --> $DIR/intermixed-lifetime.rs:10:28 - | -LL | struct Bar(&'a (), T); - | -----------------^----------- help: reorder the parameters: lifetimes, then types, then consts: `<'a, T = u32, const N: usize>` + | --------------------------^^- help: reorder the parameters: lifetimes, then types, then consts: `<'a, const N: usize, T = u32>` -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors diff --git a/src/test/ui/const-generics/defaults/intermixed-lifetime.rs b/src/test/ui/const-generics/defaults/intermixed-lifetime.rs index 9e83bf92a59b9..307e3aaf1fbf3 100644 --- a/src/test/ui/const-generics/defaults/intermixed-lifetime.rs +++ b/src/test/ui/const-generics/defaults/intermixed-lifetime.rs @@ -1,15 +1,13 @@ -// revisions: full min // Checks that lifetimes cannot be interspersed between consts and types. +// revisions: full min #![cfg_attr(full, feature(const_generics))] -#![cfg_attr(full, allow(incomplete_features))] +#![feature(const_generics_defaults)] +#![allow(incomplete_features)] struct Foo(&'a (), T); //~^ Error lifetime parameters must be declared prior to const parameters -//[min]~^^ Error type parameters must be declared prior to const parameters struct Bar(&'a (), T); -//[full]~^ Error lifetime parameters must be declared prior to type parameters -//[min]~^^ Error type parameters must be declared prior to const parameters -//[min]~| Error lifetime parameters must be declared prior to const parameters +//~^ Error lifetime parameters must be declared prior to type parameters fn main() {} diff --git a/src/test/ui/const-generics/defaults/mismatch.stderr b/src/test/ui/const-generics/defaults/mismatch.full.stderr similarity index 90% rename from src/test/ui/const-generics/defaults/mismatch.stderr rename to src/test/ui/const-generics/defaults/mismatch.full.stderr index ff72c71c40f0f..be4f364d8ee62 100644 --- a/src/test/ui/const-generics/defaults/mismatch.stderr +++ b/src/test/ui/const-generics/defaults/mismatch.full.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/mismatch.rs:11:28 + --> $DIR/mismatch.rs:12:28 | LL | let e: Example::<13> = (); | ------------- ^^ expected struct `Example`, found `()` @@ -7,7 +7,7 @@ LL | let e: Example::<13> = (); | expected due to this error[E0308]: mismatched types - --> $DIR/mismatch.rs:13:34 + --> $DIR/mismatch.rs:14:34 | LL | let e: Example2:: = (); | ------------------- ^^ expected struct `Example2`, found `()` @@ -18,7 +18,7 @@ LL | let e: Example2:: = (); found unit type `()` error[E0308]: mismatched types - --> $DIR/mismatch.rs:15:34 + --> $DIR/mismatch.rs:16:34 | LL | let e: Example3::<13, u32> = (); | ------------------- ^^ expected struct `Example3`, found `()` @@ -29,7 +29,7 @@ LL | let e: Example3::<13, u32> = (); found unit type `()` error[E0308]: mismatched types - --> $DIR/mismatch.rs:17:28 + --> $DIR/mismatch.rs:18:28 | LL | let e: Example3::<7> = (); | ------------- ^^ expected struct `Example3`, found `()` @@ -40,7 +40,7 @@ LL | let e: Example3::<7> = (); found unit type `()` error[E0308]: mismatched types - --> $DIR/mismatch.rs:21:28 + --> $DIR/mismatch.rs:22:28 | LL | let e: Example4::<7> = (); | ------------- ^^ expected struct `Example4`, found `()` diff --git a/src/test/ui/const-generics/defaults/mismatch.min.stderr b/src/test/ui/const-generics/defaults/mismatch.min.stderr new file mode 100644 index 0000000000000..be4f364d8ee62 --- /dev/null +++ b/src/test/ui/const-generics/defaults/mismatch.min.stderr @@ -0,0 +1,52 @@ +error[E0308]: mismatched types + --> $DIR/mismatch.rs:12:28 + | +LL | let e: Example::<13> = (); + | ------------- ^^ expected struct `Example`, found `()` + | | + | expected due to this + +error[E0308]: mismatched types + --> $DIR/mismatch.rs:14:34 + | +LL | let e: Example2:: = (); + | ------------------- ^^ expected struct `Example2`, found `()` + | | + | expected due to this + | + = note: expected struct `Example2` + found unit type `()` + +error[E0308]: mismatched types + --> $DIR/mismatch.rs:16:34 + | +LL | let e: Example3::<13, u32> = (); + | ------------------- ^^ expected struct `Example3`, found `()` + | | + | expected due to this + | + = note: expected struct `Example3` + found unit type `()` + +error[E0308]: mismatched types + --> $DIR/mismatch.rs:18:28 + | +LL | let e: Example3::<7> = (); + | ------------- ^^ expected struct `Example3`, found `()` + | | + | expected due to this + | + = note: expected struct `Example3<7_usize>` + found unit type `()` + +error[E0308]: mismatched types + --> $DIR/mismatch.rs:22:28 + | +LL | let e: Example4::<7> = (); + | ------------- ^^ expected struct `Example4`, found `()` + | | + | expected due to this + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/const-generics/defaults/mismatch.rs b/src/test/ui/const-generics/defaults/mismatch.rs index d85b756f538dc..68a640c0a08b3 100644 --- a/src/test/ui/const-generics/defaults/mismatch.rs +++ b/src/test/ui/const-generics/defaults/mismatch.rs @@ -1,4 +1,5 @@ -#![feature(const_generics)] +// revisions: full min +#![cfg_attr(full, feature(const_generics))] #![feature(const_generics_defaults)] #![allow(incomplete_features)] diff --git a/src/test/ui/const-generics/defaults/pretty-printing-ast.rs b/src/test/ui/const-generics/defaults/pretty-printing-ast.rs index a25d4baca1a97..7a57950dfc924 100644 --- a/src/test/ui/const-generics/defaults/pretty-printing-ast.rs +++ b/src/test/ui/const-generics/defaults/pretty-printing-ast.rs @@ -10,4 +10,4 @@ trait Foo {} fn foo() {} -struct Range; +struct Range; diff --git a/src/test/ui/const-generics/defaults/pretty-printing-ast.stdout b/src/test/ui/const-generics/defaults/pretty-printing-ast.stdout index f7a1d2ca4b2ef..f549993c413d4 100644 --- a/src/test/ui/const-generics/defaults/pretty-printing-ast.stdout +++ b/src/test/ui/const-generics/defaults/pretty-printing-ast.stdout @@ -17,4 +17,4 @@ trait Foo { } fn foo() { } struct Range; + FROM>; diff --git a/src/test/ui/const-generics/defaults/repr-c-issue-82792.rs b/src/test/ui/const-generics/defaults/repr-c-issue-82792.rs index 18ecf46729977..c64c2974c8f8f 100644 --- a/src/test/ui/const-generics/defaults/repr-c-issue-82792.rs +++ b/src/test/ui/const-generics/defaults/repr-c-issue-82792.rs @@ -6,7 +6,7 @@ #![allow(incomplete_features)] #[repr(C)] -pub struct Loaf { +pub struct Loaf { head: [T; N], slice: [T], } diff --git a/src/test/ui/const-generics/defaults/simple-defaults.min.stderr b/src/test/ui/const-generics/defaults/simple-defaults.min.stderr deleted file mode 100644 index 0746c64ac8cf4..0000000000000 --- a/src/test/ui/const-generics/defaults/simple-defaults.min.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: type parameters must be declared prior to const parameters - --> $DIR/simple-defaults.rs:8:40 - | -LL | struct FixedOutput<'a, const N: usize, T=u32> { - | ---------------------^----- help: reorder the parameters: lifetimes, then types, then consts: `<'a, T = u32, const N: usize>` - -error: aborting due to previous error - diff --git a/src/test/ui/const-generics/defaults/simple-defaults.rs b/src/test/ui/const-generics/defaults/simple-defaults.rs index cb66c7769bb23..c003cb2c5a6ee 100644 --- a/src/test/ui/const-generics/defaults/simple-defaults.rs +++ b/src/test/ui/const-generics/defaults/simple-defaults.rs @@ -1,12 +1,12 @@ -// [full] run-pass -// revisions: min full -// Checks some basic test cases for defaults. +// run-pass +// Checks that type param defaults are allowed after const params. +// revisions: full min #![cfg_attr(full, feature(const_generics))] -#![cfg_attr(full, allow(incomplete_features))] +#![feature(const_generics_defaults)] +#![allow(incomplete_features)] #![allow(dead_code)] struct FixedOutput<'a, const N: usize, T=u32> { - //[min]~^ ERROR type parameters must be declared prior to const parameters out: &'a [T; N], } diff --git a/src/test/ui/const-generics/defaults/type-default-const-param-name.rs b/src/test/ui/const-generics/defaults/type-default-const-param-name.rs new file mode 100644 index 0000000000000..e68075ee3c627 --- /dev/null +++ b/src/test/ui/const-generics/defaults/type-default-const-param-name.rs @@ -0,0 +1,19 @@ +// check-pass +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![feature(const_generics_defaults)] +#![allow(incomplete_features)] + +struct N; + +struct Foo(T); + +impl Foo { + fn new() -> Self { + Foo(N) + } +} + +fn main() { + let Foo::<1, N>(N) = Foo::new(); +} diff --git a/src/test/ui/const-generics/defaults/wrong-order.full.stderr b/src/test/ui/const-generics/defaults/wrong-order.full.stderr index accc73134d899..eb0bcb2821556 100644 --- a/src/test/ui/const-generics/defaults/wrong-order.full.stderr +++ b/src/test/ui/const-generics/defaults/wrong-order.full.stderr @@ -1,19 +1,8 @@ error: generic parameters with a default must be trailing - --> $DIR/wrong-order.rs:4:10 + --> $DIR/wrong-order.rs:6:10 | LL | struct A { | ^ - | - = note: using type defaults and const parameters in the same parameter list is currently not permitted - -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/wrong-order.rs:2:27 - | -LL | #![cfg_attr(full, feature(const_generics))] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information -error: aborting due to previous error; 1 warning emitted +error: aborting due to previous error diff --git a/src/test/ui/const-generics/defaults/wrong-order.min.stderr b/src/test/ui/const-generics/defaults/wrong-order.min.stderr index c8f1d471b244b..eb0bcb2821556 100644 --- a/src/test/ui/const-generics/defaults/wrong-order.min.stderr +++ b/src/test/ui/const-generics/defaults/wrong-order.min.stderr @@ -1,10 +1,8 @@ error: generic parameters with a default must be trailing - --> $DIR/wrong-order.rs:4:10 + --> $DIR/wrong-order.rs:6:10 | LL | struct A { | ^ - | - = note: using type defaults and const parameters in the same parameter list is currently not permitted error: aborting due to previous error diff --git a/src/test/ui/const-generics/defaults/wrong-order.rs b/src/test/ui/const-generics/defaults/wrong-order.rs index 5c2d9b8ad4751..88e9e96ba43f9 100644 --- a/src/test/ui/const-generics/defaults/wrong-order.rs +++ b/src/test/ui/const-generics/defaults/wrong-order.rs @@ -1,5 +1,7 @@ // revisions: full min -#![cfg_attr(full, feature(const_generics))] //[full]~WARN the feature `const_generics` is incomplete +#![cfg_attr(full, feature(const_generics))] +#![feature(const_generics_defaults)] +#![allow(incomplete_features)] struct A { //~^ ERROR generic parameters with a default must be trailing diff --git a/src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.full.stderr b/src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.full.stderr index c2b7b206653a6..e8fd9e7769b79 100644 --- a/src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.full.stderr +++ b/src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.full.stderr @@ -1,22 +1,17 @@ error: generic parameters with a default must be trailing - --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:11:12 + --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:8:12 | LL | struct Bar(T); | ^ | = note: using type defaults and const parameters in the same parameter list is currently not permitted -error: constant values inside of type parameter defaults must not depend on generic parameters - --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:6:44 - | -LL | struct Foo()]>(T, U); - | ^ the anonymous constant must not depend on the parameter `T` - -error: constant values inside of type parameter defaults must not depend on generic parameters - --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:11:21 +error[E0128]: generic parameters with a default cannot use forward declared identifiers + --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:8:21 | LL | struct Bar(T); - | ^ the anonymous constant must not depend on the parameter `N` + | ^ defaulted generic parameters cannot be forward declared -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0128`. diff --git a/src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.min.stderr b/src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.min.stderr index 4a462c328bf64..5fa6423306c5a 100644 --- a/src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.min.stderr +++ b/src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.min.stderr @@ -1,5 +1,5 @@ error: generic parameters with a default must be trailing - --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:11:12 + --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:8:12 | LL | struct Bar(T); | ^ @@ -7,7 +7,7 @@ LL | struct Bar(T); = note: using type defaults and const parameters in the same parameter list is currently not permitted error: generic parameters may not be used in const operations - --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:6:44 + --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:5:44 | LL | struct Foo()]>(T, U); | ^ cannot perform const operation using `T` @@ -15,11 +15,12 @@ LL | struct Foo()]>(T, U); = note: type parameters may not be used in const expressions = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions -error: constant values inside of type parameter defaults must not depend on generic parameters - --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:11:21 +error[E0128]: generic parameters with a default cannot use forward declared identifiers + --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:8:21 | LL | struct Bar(T); - | ^ the anonymous constant must not depend on the parameter `N` + | ^ defaulted generic parameters cannot be forward declared error: aborting due to 3 previous errors +For more information about this error, try `rustc --explain E0128`. diff --git a/src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.rs b/src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.rs index c7be8bdaf9c3d..76c1b84aef557 100644 --- a/src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.rs +++ b/src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.rs @@ -1,15 +1,12 @@ // revisions: full min - #![cfg_attr(full, feature(const_generics))] #![cfg_attr(full, allow(incomplete_features))] struct Foo()]>(T, U); -//[full]~^ ERROR constant values inside of type parameter defaults -//[min]~^^ ERROR generic parameters may not be used in const operations +//[min]~^ ERROR generic parameters may not be used in const operations -// FIXME(const_generics_defaults): We still don't know how to deal with type defaults. struct Bar(T); -//~^ ERROR constant values inside of type parameter defaults +//~^ ERROR generic parameters with a default cannot use forward declared identifiers //~| ERROR generic parameters with a default fn main() {} diff --git a/src/test/ui/derive-uninhabited-enum-38885.stderr b/src/test/ui/derive-uninhabited-enum-38885.stderr index 72607629d3c10..ff8fb9953fb53 100644 --- a/src/test/ui/derive-uninhabited-enum-38885.stderr +++ b/src/test/ui/derive-uninhabited-enum-38885.stderr @@ -2,8 +2,11 @@ warning: variant is never constructed: `Void` --> $DIR/derive-uninhabited-enum-38885.rs:13:5 | LL | Void(Void), - | ^^^^^^^^^^ + | ----^^^^^^ + | | + | help: if this is intentional, prefix it with an underscore: `_Void` | + = note: the leading underscore signals that this variant serves some other purpose even if it isn't used in a way that we can detect. = note: `-W dead-code` implied by `-W unused` warning: 1 warning emitted diff --git a/src/test/ui/generics/generic-non-trailing-defaults.rs b/src/test/ui/generics/generic-non-trailing-defaults.rs index cb2bb2832b70e..16ea71d48c825 100644 --- a/src/test/ui/generics/generic-non-trailing-defaults.rs +++ b/src/test/ui/generics/generic-non-trailing-defaults.rs @@ -5,5 +5,6 @@ struct Vec(A, T); struct Foo, C>(A, B, C); //~^ ERROR generic parameters with a default must be trailing +//~| ERROR generic parameters with a default cannot use fn main() {} diff --git a/src/test/ui/generics/generic-non-trailing-defaults.stderr b/src/test/ui/generics/generic-non-trailing-defaults.stderr index 6d76861750335..713ba091b861c 100644 --- a/src/test/ui/generics/generic-non-trailing-defaults.stderr +++ b/src/test/ui/generics/generic-non-trailing-defaults.stderr @@ -10,5 +10,12 @@ error: generic parameters with a default must be trailing LL | struct Foo, C>(A, B, C); | ^ -error: aborting due to 2 previous errors +error[E0128]: generic parameters with a default cannot use forward declared identifiers + --> $DIR/generic-non-trailing-defaults.rs:6:23 + | +LL | struct Foo, C>(A, B, C); + | ^ defaulted generic parameters cannot be forward declared + +error: aborting due to 3 previous errors +For more information about this error, try `rustc --explain E0128`. diff --git a/src/test/ui/issues/issue-37515.stderr b/src/test/ui/issues/issue-37515.stderr index 204a39bc8e8e9..3223554a5b778 100644 --- a/src/test/ui/issues/issue-37515.stderr +++ b/src/test/ui/issues/issue-37515.stderr @@ -2,8 +2,11 @@ warning: type alias is never used: `Z` --> $DIR/issue-37515.rs:5:1 | LL | type Z = dyn for<'x> Send; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^-^^^^^^^^^^^^^^^^^^^^ + | | + | help: if this is intentional, prefix it with an underscore: `_Z` | + = note: the leading underscore signals that this type alias serves some other purpose even if it isn't used in a way that we can detect. note: the lint level is defined here --> $DIR/issue-37515.rs:3:9 | diff --git a/src/test/ui/lint/dead-code/basic.stderr b/src/test/ui/lint/dead-code/basic.stderr index f7b9b9c613ae0..8264d0736e03e 100644 --- a/src/test/ui/lint/dead-code/basic.stderr +++ b/src/test/ui/lint/dead-code/basic.stderr @@ -2,8 +2,9 @@ error: function is never used: `foo` --> $DIR/basic.rs:4:4 | LL | fn foo() { - | ^^^ + | ^^^ help: if this is intentional, prefix it with an underscore: `_foo` | + = note: the leading underscore signals that this function serves some other purpose even if it isn't used in a way that we can detect. note: the lint level is defined here --> $DIR/basic.rs:1:9 | diff --git a/src/test/ui/lint/dead-code/const-and-self.stderr b/src/test/ui/lint/dead-code/const-and-self.stderr index c0e406189e8ab..e7233f386cc68 100644 --- a/src/test/ui/lint/dead-code/const-and-self.stderr +++ b/src/test/ui/lint/dead-code/const-and-self.stderr @@ -2,8 +2,9 @@ warning: variant is never constructed: `B` --> $DIR/const-and-self.rs:33:5 | LL | B, - | ^ + | ^ help: if this is intentional, prefix it with an underscore: `_B` | + = note: the leading underscore signals that this variant serves some other purpose even if it isn't used in a way that we can detect. note: the lint level is defined here --> $DIR/const-and-self.rs:3:9 | @@ -14,7 +15,9 @@ warning: variant is never constructed: `C` --> $DIR/const-and-self.rs:34:5 | LL | C, - | ^ + | ^ help: if this is intentional, prefix it with an underscore: `_C` + | + = note: the leading underscore signals that this variant serves some other purpose even if it isn't used in a way that we can detect. warning: 2 warnings emitted diff --git a/src/test/ui/lint/dead-code/drop-only-field-issue-81658.rs b/src/test/ui/lint/dead-code/drop-only-field-issue-81658.rs new file mode 100644 index 0000000000000..d28b6430bc587 --- /dev/null +++ b/src/test/ui/lint/dead-code/drop-only-field-issue-81658.rs @@ -0,0 +1,42 @@ +//! The field `guard` is never used directly, but it is still useful for its side effect when +//! dropped. Since rustc doesn't consider a `Drop` impl as a use, we want to make sure we at least +//! produce a helpful diagnostic that points the user to what they can do if they indeed intended to +//! have a field that is only used for its `Drop` side effect. +//! +//! Issue: https://github.com/rust-lang/rust/issues/81658 + +#![deny(dead_code)] + +use std::sync::{Mutex, MutexGuard}; + +/// Holds a locked value until it is dropped +pub struct Locked<'a, T> { + // Field is kept for its affect when dropped, but otherwise unused + guard: MutexGuard<'a, T>, //~ ERROR field is never read +} + +impl<'a, T> Locked<'a, T> { + pub fn new(value: &'a Mutex) -> Self { + Self { + guard: value.lock().unwrap(), + } + } +} + +fn main() { + let items = Mutex::new(vec![1, 2, 3]); + + // Hold a lock on items while doing something else + let result = { + // The lock will be released at the end of this scope + let _lock = Locked::new(&items); + + do_something_else() + }; + + println!("{}", result); +} + +fn do_something_else() -> i32 { + 1 + 1 +} diff --git a/src/test/ui/lint/dead-code/drop-only-field-issue-81658.stderr b/src/test/ui/lint/dead-code/drop-only-field-issue-81658.stderr new file mode 100644 index 0000000000000..f379a0941166f --- /dev/null +++ b/src/test/ui/lint/dead-code/drop-only-field-issue-81658.stderr @@ -0,0 +1,17 @@ +error: field is never read: `guard` + --> $DIR/drop-only-field-issue-81658.rs:15:5 + | +LL | guard: MutexGuard<'a, T>, + | -----^^^^^^^^^^^^^^^^^^^ + | | + | help: if this is intentional, prefix it with an underscore: `_guard` + | + = note: the leading underscore signals that this field serves some other purpose even if it isn't used in a way that we can detect. (e.g. for its effect when dropped or in foreign code) +note: the lint level is defined here + --> $DIR/drop-only-field-issue-81658.rs:8:9 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/lint/dead-code/empty-unused-enum.stderr b/src/test/ui/lint/dead-code/empty-unused-enum.stderr index ed9a7ccd14b21..5c06cd5a6a0b2 100644 --- a/src/test/ui/lint/dead-code/empty-unused-enum.stderr +++ b/src/test/ui/lint/dead-code/empty-unused-enum.stderr @@ -2,8 +2,9 @@ error: enum is never used: `E` --> $DIR/empty-unused-enum.rs:3:6 | LL | enum E {} - | ^ + | ^ help: if this is intentional, prefix it with an underscore: `_E` | + = note: the leading underscore signals that this enum serves some other purpose even if it isn't used in a way that we can detect. note: the lint level is defined here --> $DIR/empty-unused-enum.rs:1:9 | diff --git a/src/test/ui/lint/dead-code/field-used-in-ffi-issue-81658.rs b/src/test/ui/lint/dead-code/field-used-in-ffi-issue-81658.rs new file mode 100644 index 0000000000000..12eafe6ae4958 --- /dev/null +++ b/src/test/ui/lint/dead-code/field-used-in-ffi-issue-81658.rs @@ -0,0 +1,50 @@ +//! The field `items` is being "used" by FFI (implicitly through pointers). However, since rustc +//! doesn't know how to detect that, we produce a message that says the field is unused. This can +//! cause some confusion and we want to make sure our diagnostics help as much as they can. +//! +//! Issue: https://github.com/rust-lang/rust/issues/81658 + +#![deny(dead_code)] + +/// A struct for holding on to data while it is being used in our FFI code +pub struct FFIData { + /// These values cannot be dropped while the pointers to each item + /// are still in use + items: Option>, //~ ERROR field is never read +} + +impl FFIData { + pub fn new() -> Self { + Self {items: None} + } + + /// Load items into this type and return pointers to each item that can + /// be passed to FFI + pub fn load(&mut self, items: Vec) -> Vec<*const T> { + let ptrs = items.iter().map(|item| item as *const _).collect(); + + self.items = Some(items); + + ptrs + } +} + +extern { + /// The FFI code that uses items + fn process_item(item: *const i32); +} + +fn main() { + // Data cannot be dropped until the end of this scope or else the items + // will be dropped before they are processed + let mut data = FFIData::new(); + + let ptrs = data.load(vec![1, 2, 3, 4, 5]); + + for ptr in ptrs { + // Safety: This pointer is valid as long as the arena is in scope + unsafe { process_item(ptr); } + } + + // Items will be safely freed at the end of this scope +} diff --git a/src/test/ui/lint/dead-code/field-used-in-ffi-issue-81658.stderr b/src/test/ui/lint/dead-code/field-used-in-ffi-issue-81658.stderr new file mode 100644 index 0000000000000..d6a247d98e292 --- /dev/null +++ b/src/test/ui/lint/dead-code/field-used-in-ffi-issue-81658.stderr @@ -0,0 +1,17 @@ +error: field is never read: `items` + --> $DIR/field-used-in-ffi-issue-81658.rs:13:5 + | +LL | items: Option>, + | -----^^^^^^^^^^^^^^^^ + | | + | help: if this is intentional, prefix it with an underscore: `_items` + | + = note: the leading underscore signals that this field serves some other purpose even if it isn't used in a way that we can detect. (e.g. for its effect when dropped or in foreign code) +note: the lint level is defined here + --> $DIR/field-used-in-ffi-issue-81658.rs:7:9 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/lint/dead-code/impl-trait.stderr b/src/test/ui/lint/dead-code/impl-trait.stderr index 09b6d08eb8fb8..fb18eb2819f76 100644 --- a/src/test/ui/lint/dead-code/impl-trait.stderr +++ b/src/test/ui/lint/dead-code/impl-trait.stderr @@ -2,8 +2,11 @@ error: type alias is never used: `Unused` --> $DIR/impl-trait.rs:12:1 | LL | type Unused = (); - | ^^^^^^^^^^^^^^^^^ + | ^^^^^------^^^^^^ + | | + | help: if this is intentional, prefix it with an underscore: `_Unused` | + = note: the leading underscore signals that this type alias serves some other purpose even if it isn't used in a way that we can detect. note: the lint level is defined here --> $DIR/impl-trait.rs:1:9 | diff --git a/src/test/ui/lint/dead-code/lint-dead-code-1.stderr b/src/test/ui/lint/dead-code/lint-dead-code-1.stderr index af97ea98b2b6d..15448448e1169 100644 --- a/src/test/ui/lint/dead-code/lint-dead-code-1.stderr +++ b/src/test/ui/lint/dead-code/lint-dead-code-1.stderr @@ -2,8 +2,9 @@ error: struct is never constructed: `Bar` --> $DIR/lint-dead-code-1.rs:12:16 | LL | pub struct Bar; - | ^^^ + | ^^^ help: if this is intentional, prefix it with an underscore: `_Bar` | + = note: the leading underscore signals that this struct serves some other purpose even if it isn't used in a way that we can detect. note: the lint level is defined here --> $DIR/lint-dead-code-1.rs:5:9 | @@ -14,55 +15,77 @@ error: static is never used: `priv_static` --> $DIR/lint-dead-code-1.rs:20:1 | LL | static priv_static: isize = 0; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^-----------^^^^^^^^^^^^ + | | + | help: if this is intentional, prefix it with an underscore: `_priv_static` + | + = note: the leading underscore signals that this static serves some other purpose even if it isn't used in a way that we can detect. error: constant is never used: `priv_const` --> $DIR/lint-dead-code-1.rs:27:1 | LL | const priv_const: isize = 0; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^----------^^^^^^^^^^^^ + | | + | help: if this is intentional, prefix it with an underscore: `_priv_const` + | + = note: the leading underscore signals that this constant serves some other purpose even if it isn't used in a way that we can detect. error: struct is never constructed: `PrivStruct` --> $DIR/lint-dead-code-1.rs:35:8 | LL | struct PrivStruct; - | ^^^^^^^^^^ + | ^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_PrivStruct` + | + = note: the leading underscore signals that this struct serves some other purpose even if it isn't used in a way that we can detect. error: enum is never used: `priv_enum` --> $DIR/lint-dead-code-1.rs:64:6 | LL | enum priv_enum { foo2, bar2 } - | ^^^^^^^^^ + | ^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_priv_enum` + | + = note: the leading underscore signals that this enum serves some other purpose even if it isn't used in a way that we can detect. error: variant is never constructed: `bar3` --> $DIR/lint-dead-code-1.rs:67:5 | LL | bar3 - | ^^^^ + | ^^^^ help: if this is intentional, prefix it with an underscore: `_bar3` + | + = note: the leading underscore signals that this variant serves some other purpose even if it isn't used in a way that we can detect. error: function is never used: `priv_fn` --> $DIR/lint-dead-code-1.rs:88:4 | LL | fn priv_fn() { - | ^^^^^^^ + | ^^^^^^^ help: if this is intentional, prefix it with an underscore: `_priv_fn` + | + = note: the leading underscore signals that this function serves some other purpose even if it isn't used in a way that we can detect. error: function is never used: `foo` --> $DIR/lint-dead-code-1.rs:93:4 | LL | fn foo() { - | ^^^ + | ^^^ help: if this is intentional, prefix it with an underscore: `_foo` + | + = note: the leading underscore signals that this function serves some other purpose even if it isn't used in a way that we can detect. error: function is never used: `bar` --> $DIR/lint-dead-code-1.rs:98:4 | LL | fn bar() { - | ^^^ + | ^^^ help: if this is intentional, prefix it with an underscore: `_bar` + | + = note: the leading underscore signals that this function serves some other purpose even if it isn't used in a way that we can detect. error: function is never used: `baz` --> $DIR/lint-dead-code-1.rs:102:4 | LL | fn baz() -> impl Copy { - | ^^^ + | ^^^ help: if this is intentional, prefix it with an underscore: `_baz` + | + = note: the leading underscore signals that this function serves some other purpose even if it isn't used in a way that we can detect. error: aborting due to 10 previous errors diff --git a/src/test/ui/lint/dead-code/lint-dead-code-2.stderr b/src/test/ui/lint/dead-code/lint-dead-code-2.stderr index b01ba57f98580..5e19c7d02ffd3 100644 --- a/src/test/ui/lint/dead-code/lint-dead-code-2.stderr +++ b/src/test/ui/lint/dead-code/lint-dead-code-2.stderr @@ -2,8 +2,9 @@ error: function is never used: `dead_fn` --> $DIR/lint-dead-code-2.rs:22:4 | LL | fn dead_fn() {} - | ^^^^^^^ + | ^^^^^^^ help: if this is intentional, prefix it with an underscore: `_dead_fn` | + = note: the leading underscore signals that this function serves some other purpose even if it isn't used in a way that we can detect. note: the lint level is defined here --> $DIR/lint-dead-code-2.rs:2:9 | @@ -14,13 +15,17 @@ error: function is never used: `dead_fn2` --> $DIR/lint-dead-code-2.rs:25:4 | LL | fn dead_fn2() {} - | ^^^^^^^^ + | ^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_dead_fn2` + | + = note: the leading underscore signals that this function serves some other purpose even if it isn't used in a way that we can detect. error: function is never used: `main` --> $DIR/lint-dead-code-2.rs:38:4 | LL | fn main() { - | ^^^^ + | ^^^^ help: if this is intentional, prefix it with an underscore: `_main` + | + = note: the leading underscore signals that this function serves some other purpose even if it isn't used in a way that we can detect. error: aborting due to 3 previous errors diff --git a/src/test/ui/lint/dead-code/lint-dead-code-3.stderr b/src/test/ui/lint/dead-code/lint-dead-code-3.stderr index cf8f01ea19f0c..d32fde5872d99 100644 --- a/src/test/ui/lint/dead-code/lint-dead-code-3.stderr +++ b/src/test/ui/lint/dead-code/lint-dead-code-3.stderr @@ -2,8 +2,9 @@ error: struct is never constructed: `Foo` --> $DIR/lint-dead-code-3.rs:14:8 | LL | struct Foo; - | ^^^ + | ^^^ help: if this is intentional, prefix it with an underscore: `_Foo` | + = note: the leading underscore signals that this struct serves some other purpose even if it isn't used in a way that we can detect. note: the lint level is defined here --> $DIR/lint-dead-code-3.rs:4:9 | @@ -14,25 +15,35 @@ error: associated function is never used: `foo` --> $DIR/lint-dead-code-3.rs:16:8 | LL | fn foo(&self) { - | ^^^ + | ^^^ help: if this is intentional, prefix it with an underscore: `_foo` + | + = note: the leading underscore signals that this associated function serves some other purpose even if it isn't used in a way that we can detect. error: function is never used: `bar` --> $DIR/lint-dead-code-3.rs:21:4 | LL | fn bar() { - | ^^^ + | ^^^ help: if this is intentional, prefix it with an underscore: `_bar` + | + = note: the leading underscore signals that this function serves some other purpose even if it isn't used in a way that we can detect. error: enum is never used: `c_void` --> $DIR/lint-dead-code-3.rs:60:6 | LL | enum c_void {} - | ^^^^^^ + | ^^^^^^ help: if this is intentional, prefix it with an underscore: `_c_void` + | + = note: the leading underscore signals that this enum serves some other purpose even if it isn't used in a way that we can detect. error: function is never used: `free` --> $DIR/lint-dead-code-3.rs:62:5 | LL | fn free(p: *const c_void); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^----^^^^^^^^^^^^^^^^^^^ + | | + | help: if this is intentional, prefix it with an underscore: `_free` + | + = note: the leading underscore signals that this function serves some other purpose even if it isn't used in a way that we can detect. error: aborting due to 5 previous errors diff --git a/src/test/ui/lint/dead-code/lint-dead-code-4.stderr b/src/test/ui/lint/dead-code/lint-dead-code-4.stderr index 3905d1a06bdfe..2785faa29f5d9 100644 --- a/src/test/ui/lint/dead-code/lint-dead-code-4.stderr +++ b/src/test/ui/lint/dead-code/lint-dead-code-4.stderr @@ -2,8 +2,11 @@ error: field is never read: `b` --> $DIR/lint-dead-code-4.rs:7:5 | LL | b: bool, - | ^^^^^^^ + | -^^^^^^ + | | + | help: if this is intentional, prefix it with an underscore: `_b` | + = note: the leading underscore signals that this field serves some other purpose even if it isn't used in a way that we can detect. (e.g. for its effect when dropped or in foreign code) note: the lint level is defined here --> $DIR/lint-dead-code-4.rs:3:9 | @@ -14,59 +17,88 @@ error: variant is never constructed: `X` --> $DIR/lint-dead-code-4.rs:15:5 | LL | X, - | ^ + | ^ help: if this is intentional, prefix it with an underscore: `_X` + | + = note: the leading underscore signals that this variant serves some other purpose even if it isn't used in a way that we can detect. error: variant is never constructed: `Y` --> $DIR/lint-dead-code-4.rs:16:5 | -LL | / Y { +LL | Y { + | ^ help: if this is intentional, prefix it with an underscore: `_Y` + | _____| + | | LL | | a: String, LL | | b: i32, LL | | c: i32, LL | | }, | |_____^ + | + = note: the leading underscore signals that this variant serves some other purpose even if it isn't used in a way that we can detect. error: enum is never used: `ABC` --> $DIR/lint-dead-code-4.rs:24:6 | LL | enum ABC { - | ^^^ + | ^^^ help: if this is intentional, prefix it with an underscore: `_ABC` + | + = note: the leading underscore signals that this enum serves some other purpose even if it isn't used in a way that we can detect. error: variant is never constructed: `I` --> $DIR/lint-dead-code-4.rs:36:5 | LL | I, - | ^ + | ^ help: if this is intentional, prefix it with an underscore: `_I` + | + = note: the leading underscore signals that this variant serves some other purpose even if it isn't used in a way that we can detect. error: field is never read: `b` --> $DIR/lint-dead-code-4.rs:39:9 | LL | b: i32, - | ^^^^^^ + | -^^^^^ + | | + | help: if this is intentional, prefix it with an underscore: `_b` + | + = note: the leading underscore signals that this field serves some other purpose even if it isn't used in a way that we can detect. (e.g. for its effect when dropped or in foreign code) error: field is never read: `c` --> $DIR/lint-dead-code-4.rs:40:9 | LL | c: i32, - | ^^^^^^ + | -^^^^^ + | | + | help: if this is intentional, prefix it with an underscore: `_c` + | + = note: the leading underscore signals that this field serves some other purpose even if it isn't used in a way that we can detect. (e.g. for its effect when dropped or in foreign code) error: variant is never constructed: `K` --> $DIR/lint-dead-code-4.rs:42:5 | LL | K - | ^ + | ^ help: if this is intentional, prefix it with an underscore: `_K` + | + = note: the leading underscore signals that this variant serves some other purpose even if it isn't used in a way that we can detect. error: field is never read: `x` --> $DIR/lint-dead-code-4.rs:61:5 | LL | x: usize, - | ^^^^^^^^ + | -^^^^^^^ + | | + | help: if this is intentional, prefix it with an underscore: `_x` + | + = note: the leading underscore signals that this field serves some other purpose even if it isn't used in a way that we can detect. (e.g. for its effect when dropped or in foreign code) error: field is never read: `c` --> $DIR/lint-dead-code-4.rs:63:5 | LL | c: bool, - | ^^^^^^^ + | -^^^^^^ + | | + | help: if this is intentional, prefix it with an underscore: `_c` + | + = note: the leading underscore signals that this field serves some other purpose even if it isn't used in a way that we can detect. (e.g. for its effect when dropped or in foreign code) error: aborting due to 10 previous errors diff --git a/src/test/ui/lint/dead-code/lint-dead-code-5.stderr b/src/test/ui/lint/dead-code/lint-dead-code-5.stderr index 519add826273f..6375d98d35cb2 100644 --- a/src/test/ui/lint/dead-code/lint-dead-code-5.stderr +++ b/src/test/ui/lint/dead-code/lint-dead-code-5.stderr @@ -2,8 +2,9 @@ error: variant is never constructed: `Variant2` --> $DIR/lint-dead-code-5.rs:6:5 | LL | Variant2 - | ^^^^^^^^ + | ^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_Variant2` | + = note: the leading underscore signals that this variant serves some other purpose even if it isn't used in a way that we can detect. note: the lint level is defined here --> $DIR/lint-dead-code-5.rs:2:9 | @@ -14,19 +15,29 @@ error: variant is never constructed: `Variant5` --> $DIR/lint-dead-code-5.rs:13:5 | LL | Variant5 { _x: isize }, - | ^^^^^^^^^^^^^^^^^^^^^^ + | --------^^^^^^^^^^^^^^ + | | + | help: if this is intentional, prefix it with an underscore: `_Variant5` + | + = note: the leading underscore signals that this variant serves some other purpose even if it isn't used in a way that we can detect. error: variant is never constructed: `Variant6` --> $DIR/lint-dead-code-5.rs:14:5 | LL | Variant6(isize), - | ^^^^^^^^^^^^^^^ + | --------^^^^^^^ + | | + | help: if this is intentional, prefix it with an underscore: `_Variant6` + | + = note: the leading underscore signals that this variant serves some other purpose even if it isn't used in a way that we can detect. error: enum is never used: `Enum3` --> $DIR/lint-dead-code-5.rs:35:6 | LL | enum Enum3 { - | ^^^^^ + | ^^^^^ help: if this is intentional, prefix it with an underscore: `_Enum3` + | + = note: the leading underscore signals that this enum serves some other purpose even if it isn't used in a way that we can detect. error: aborting due to 4 previous errors diff --git a/src/test/ui/lint/dead-code/lint-dead-code-6.stderr b/src/test/ui/lint/dead-code/lint-dead-code-6.stderr index 7dc60730d6aad..ef26fe54ab589 100644 --- a/src/test/ui/lint/dead-code/lint-dead-code-6.stderr +++ b/src/test/ui/lint/dead-code/lint-dead-code-6.stderr @@ -2,8 +2,9 @@ error: struct is never constructed: `UnusedStruct` --> $DIR/lint-dead-code-6.rs:3:8 | LL | struct UnusedStruct; - | ^^^^^^^^^^^^ + | ^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_UnusedStruct` | + = note: the leading underscore signals that this struct serves some other purpose even if it isn't used in a way that we can detect. note: the lint level is defined here --> $DIR/lint-dead-code-6.rs:1:9 | @@ -14,19 +15,25 @@ error: associated function is never used: `unused_impl_fn_1` --> $DIR/lint-dead-code-6.rs:5:8 | LL | fn unused_impl_fn_1() { - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_unused_impl_fn_1` + | + = note: the leading underscore signals that this associated function serves some other purpose even if it isn't used in a way that we can detect. error: associated function is never used: `unused_impl_fn_2` --> $DIR/lint-dead-code-6.rs:9:8 | LL | fn unused_impl_fn_2(var: i32) { - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_unused_impl_fn_2` + | + = note: the leading underscore signals that this associated function serves some other purpose even if it isn't used in a way that we can detect. error: associated function is never used: `unused_impl_fn_3` --> $DIR/lint-dead-code-6.rs:13:8 | LL | fn unused_impl_fn_3( - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_unused_impl_fn_3` + | + = note: the leading underscore signals that this associated function serves some other purpose even if it isn't used in a way that we can detect. error: aborting due to 4 previous errors diff --git a/src/test/ui/lint/dead-code/newline-span.stderr b/src/test/ui/lint/dead-code/newline-span.stderr index fd74405f2b648..5bd566be35e01 100644 --- a/src/test/ui/lint/dead-code/newline-span.stderr +++ b/src/test/ui/lint/dead-code/newline-span.stderr @@ -2,8 +2,9 @@ error: function is never used: `unused` --> $DIR/newline-span.rs:3:4 | LL | fn unused() { - | ^^^^^^ + | ^^^^^^ help: if this is intentional, prefix it with an underscore: `_unused` | + = note: the leading underscore signals that this function serves some other purpose even if it isn't used in a way that we can detect. note: the lint level is defined here --> $DIR/newline-span.rs:1:9 | @@ -14,13 +15,17 @@ error: function is never used: `unused2` --> $DIR/newline-span.rs:7:4 | LL | fn unused2(var: i32) { - | ^^^^^^^ + | ^^^^^^^ help: if this is intentional, prefix it with an underscore: `_unused2` + | + = note: the leading underscore signals that this function serves some other purpose even if it isn't used in a way that we can detect. error: function is never used: `unused3` --> $DIR/newline-span.rs:11:4 | LL | fn unused3( - | ^^^^^^^ + | ^^^^^^^ help: if this is intentional, prefix it with an underscore: `_unused3` + | + = note: the leading underscore signals that this function serves some other purpose even if it isn't used in a way that we can detect. error: aborting due to 3 previous errors diff --git a/src/test/ui/lint/dead-code/type-alias.stderr b/src/test/ui/lint/dead-code/type-alias.stderr index b2acd5d4213b3..1e7a030de3aa7 100644 --- a/src/test/ui/lint/dead-code/type-alias.stderr +++ b/src/test/ui/lint/dead-code/type-alias.stderr @@ -2,8 +2,11 @@ error: type alias is never used: `Unused` --> $DIR/type-alias.rs:4:1 | LL | type Unused = u8; - | ^^^^^^^^^^^^^^^^^ + | ^^^^^------^^^^^^ + | | + | help: if this is intentional, prefix it with an underscore: `_Unused` | + = note: the leading underscore signals that this type alias serves some other purpose even if it isn't used in a way that we can detect. note: the lint level is defined here --> $DIR/type-alias.rs:1:9 | diff --git a/src/test/ui/lint/dead-code/unused-enum.stderr b/src/test/ui/lint/dead-code/unused-enum.stderr index 9f368fdd2f816..d536479c800ae 100644 --- a/src/test/ui/lint/dead-code/unused-enum.stderr +++ b/src/test/ui/lint/dead-code/unused-enum.stderr @@ -2,8 +2,9 @@ error: struct is never constructed: `F` --> $DIR/unused-enum.rs:3:8 | LL | struct F; - | ^ + | ^ help: if this is intentional, prefix it with an underscore: `_F` | + = note: the leading underscore signals that this struct serves some other purpose even if it isn't used in a way that we can detect. note: the lint level is defined here --> $DIR/unused-enum.rs:1:9 | @@ -15,13 +16,17 @@ error: struct is never constructed: `B` --> $DIR/unused-enum.rs:4:8 | LL | struct B; - | ^ + | ^ help: if this is intentional, prefix it with an underscore: `_B` + | + = note: the leading underscore signals that this struct serves some other purpose even if it isn't used in a way that we can detect. error: enum is never used: `E` --> $DIR/unused-enum.rs:6:6 | LL | enum E { - | ^ + | ^ help: if this is intentional, prefix it with an underscore: `_E` + | + = note: the leading underscore signals that this enum serves some other purpose even if it isn't used in a way that we can detect. error: aborting due to 3 previous errors diff --git a/src/test/ui/lint/dead-code/unused-struct-variant.stderr b/src/test/ui/lint/dead-code/unused-struct-variant.stderr index b93d6d4ac1866..394ced3e81001 100644 --- a/src/test/ui/lint/dead-code/unused-struct-variant.stderr +++ b/src/test/ui/lint/dead-code/unused-struct-variant.stderr @@ -2,8 +2,11 @@ error: variant is never constructed: `Bar` --> $DIR/unused-struct-variant.rs:8:5 | LL | Bar(B), - | ^^^^^^ + | ---^^^ + | | + | help: if this is intentional, prefix it with an underscore: `_Bar` | + = note: the leading underscore signals that this variant serves some other purpose even if it isn't used in a way that we can detect. note: the lint level is defined here --> $DIR/unused-struct-variant.rs:1:9 | diff --git a/src/test/ui/lint/dead-code/unused-variant.stderr b/src/test/ui/lint/dead-code/unused-variant.stderr index a547f5af4b082..7dcb79d0490c1 100644 --- a/src/test/ui/lint/dead-code/unused-variant.stderr +++ b/src/test/ui/lint/dead-code/unused-variant.stderr @@ -2,8 +2,9 @@ error: variant is never constructed: `Variant1` --> $DIR/unused-variant.rs:5:5 | LL | Variant1, - | ^^^^^^^^ + | ^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_Variant1` | + = note: the leading underscore signals that this variant serves some other purpose even if it isn't used in a way that we can detect. note: the lint level is defined here --> $DIR/unused-variant.rs:1:9 | diff --git a/src/test/ui/lint/dead-code/with-core-crate.stderr b/src/test/ui/lint/dead-code/with-core-crate.stderr index 2c63e60d67609..1bde434069ee7 100644 --- a/src/test/ui/lint/dead-code/with-core-crate.stderr +++ b/src/test/ui/lint/dead-code/with-core-crate.stderr @@ -2,8 +2,9 @@ error: function is never used: `foo` --> $DIR/with-core-crate.rs:7:4 | LL | fn foo() { - | ^^^ + | ^^^ help: if this is intentional, prefix it with an underscore: `_foo` | + = note: the leading underscore signals that this function serves some other purpose even if it isn't used in a way that we can detect. note: the lint level is defined here --> $DIR/with-core-crate.rs:1:9 | diff --git a/src/test/ui/lint/dead-code/write-only-field.stderr b/src/test/ui/lint/dead-code/write-only-field.stderr index a191d22c8b94c..13a21bb1193de 100644 --- a/src/test/ui/lint/dead-code/write-only-field.stderr +++ b/src/test/ui/lint/dead-code/write-only-field.stderr @@ -2,8 +2,11 @@ error: field is never read: `f` --> $DIR/write-only-field.rs:4:5 | LL | f: i32, - | ^^^^^^ + | -^^^^^ + | | + | help: if this is intentional, prefix it with an underscore: `_f` | + = note: the leading underscore signals that this field serves some other purpose even if it isn't used in a way that we can detect. (e.g. for its effect when dropped or in foreign code) note: the lint level is defined here --> $DIR/write-only-field.rs:1:9 | @@ -14,31 +17,51 @@ error: field is never read: `sub` --> $DIR/write-only-field.rs:5:5 | LL | sub: Sub, - | ^^^^^^^^ + | ---^^^^^ + | | + | help: if this is intentional, prefix it with an underscore: `_sub` + | + = note: the leading underscore signals that this field serves some other purpose even if it isn't used in a way that we can detect. (e.g. for its effect when dropped or in foreign code) error: field is never read: `f` --> $DIR/write-only-field.rs:9:5 | LL | f: i32, - | ^^^^^^ + | -^^^^^ + | | + | help: if this is intentional, prefix it with an underscore: `_f` + | + = note: the leading underscore signals that this field serves some other purpose even if it isn't used in a way that we can detect. (e.g. for its effect when dropped or in foreign code) error: field is never read: `y` --> $DIR/write-only-field.rs:28:9 | LL | y: bool, - | ^^^^^^^ + | -^^^^^^ + | | + | help: if this is intentional, prefix it with an underscore: `_y` + | + = note: the leading underscore signals that this field serves some other purpose even if it isn't used in a way that we can detect. (e.g. for its effect when dropped or in foreign code) error: field is never read: `u` --> $DIR/write-only-field.rs:58:9 | LL | u: u32, - | ^^^^^^ + | -^^^^^ + | | + | help: if this is intentional, prefix it with an underscore: `_u` + | + = note: the leading underscore signals that this field serves some other purpose even if it isn't used in a way that we can detect. (e.g. for its effect when dropped or in foreign code) error: field is never read: `v` --> $DIR/write-only-field.rs:59:9 | LL | v: u32, - | ^^^^^^ + | -^^^^^ + | | + | help: if this is intentional, prefix it with an underscore: `_v` + | + = note: the leading underscore signals that this field serves some other purpose even if it isn't used in a way that we can detect. (e.g. for its effect when dropped or in foreign code) error: aborting due to 6 previous errors diff --git a/src/test/ui/lint/issue-17718-const-naming.stderr b/src/test/ui/lint/issue-17718-const-naming.stderr index ce4ebcb5e3ef6..e880006e114c1 100644 --- a/src/test/ui/lint/issue-17718-const-naming.stderr +++ b/src/test/ui/lint/issue-17718-const-naming.stderr @@ -2,8 +2,11 @@ error: constant is never used: `foo` --> $DIR/issue-17718-const-naming.rs:4:1 | LL | const foo: isize = 3; - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^---^^^^^^^^^^^^ + | | + | help: if this is intentional, prefix it with an underscore: `_foo` | + = note: the leading underscore signals that this constant serves some other purpose even if it isn't used in a way that we can detect. note: the lint level is defined here --> $DIR/issue-17718-const-naming.rs:2:9 | diff --git a/src/test/ui/resolve/issue-3907.stderr b/src/test/ui/resolve/issue-3907.stderr index 4d0b0af58a320..6fc61cae84339 100644 --- a/src/test/ui/resolve/issue-3907.stderr +++ b/src/test/ui/resolve/issue-3907.stderr @@ -5,10 +5,9 @@ LL | impl Foo for S { | ^^^ type aliases cannot be used as traits | help: you might have meant to use `#![feature(trait_alias)]` instead of a `type` alias - --> $DIR/issue-3907.rs:5:1 | -LL | type Foo = dyn issue_3907::Foo; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | trait Foo = dyn issue_3907::Foo; + | help: consider importing this trait instead | LL | use issue_3907::Foo; diff --git a/src/test/ui/resolve/issue-5035.stderr b/src/test/ui/resolve/issue-5035.stderr index 41dff2fe54205..a8aa50b7c3ab2 100644 --- a/src/test/ui/resolve/issue-5035.stderr +++ b/src/test/ui/resolve/issue-5035.stderr @@ -11,16 +11,16 @@ LL | trait I {} | ------- similarly named trait `I` defined here LL | type K = dyn I; LL | impl K for isize {} - | ^ - | | - | type aliases cannot be used as traits - | help: a trait with a similar name exists: `I` + | ^ type aliases cannot be used as traits | help: you might have meant to use `#![feature(trait_alias)]` instead of a `type` alias - --> $DIR/issue-5035.rs:2:1 | -LL | type K = dyn I; - | ^^^^^^^^^^^^^^^ +LL | trait K = dyn I; + | +help: a trait with a similar name exists + | +LL | impl I for isize {} + | ^ error: aborting due to 2 previous errors diff --git a/src/test/ui/resolve/unboxed-closure-sugar-nonexistent-trait.stderr b/src/test/ui/resolve/unboxed-closure-sugar-nonexistent-trait.stderr index 2974d08eb23b1..8addc0303fb91 100644 --- a/src/test/ui/resolve/unboxed-closure-sugar-nonexistent-trait.stderr +++ b/src/test/ui/resolve/unboxed-closure-sugar-nonexistent-trait.stderr @@ -11,10 +11,9 @@ LL | fn g isize>(x: F) {} | ^^^^^^^^^^^^^^^^^^^^^^^ type aliases cannot be used as traits | help: you might have meant to use `#![feature(trait_alias)]` instead of a `type` alias - --> $DIR/unboxed-closure-sugar-nonexistent-trait.rs:4:1 | -LL | type Typedef = isize; - | ^^^^^^^^^^^^^^^^^^^^^ +LL | trait Typedef = isize; + | error: aborting due to 2 previous errors diff --git a/src/test/ui/span/macro-span-replacement.stderr b/src/test/ui/span/macro-span-replacement.stderr index 45cf5f8688cd1..f94a9e30a3dfa 100644 --- a/src/test/ui/span/macro-span-replacement.stderr +++ b/src/test/ui/span/macro-span-replacement.stderr @@ -2,11 +2,14 @@ warning: struct is never constructed: `S` --> $DIR/macro-span-replacement.rs:7:14 | LL | $b $a; - | ^ + | --^ + | | + | help: if this is intentional, prefix it with an underscore: `_S` ... LL | m!(S struct); | ------------- in this macro invocation | + = note: the leading underscore signals that this struct serves some other purpose even if it isn't used in a way that we can detect. note: the lint level is defined here --> $DIR/macro-span-replacement.rs:3:9 | diff --git a/src/test/ui/span/unused-warning-point-at-identifier.stderr b/src/test/ui/span/unused-warning-point-at-identifier.stderr index 6ef877da122f5..3bf342f197252 100644 --- a/src/test/ui/span/unused-warning-point-at-identifier.stderr +++ b/src/test/ui/span/unused-warning-point-at-identifier.stderr @@ -2,8 +2,9 @@ warning: enum is never used: `Enum` --> $DIR/unused-warning-point-at-identifier.rs:5:6 | LL | enum Enum { - | ^^^^ + | ^^^^ help: if this is intentional, prefix it with an underscore: `_Enum` | + = note: the leading underscore signals that this enum serves some other purpose even if it isn't used in a way that we can detect. note: the lint level is defined here --> $DIR/unused-warning-point-at-identifier.rs:3:9 | @@ -15,19 +16,25 @@ warning: struct is never constructed: `Struct` --> $DIR/unused-warning-point-at-identifier.rs:12:8 | LL | struct Struct { - | ^^^^^^ + | ^^^^^^ help: if this is intentional, prefix it with an underscore: `_Struct` + | + = note: the leading underscore signals that this struct serves some other purpose even if it isn't used in a way that we can detect. warning: function is never used: `func` --> $DIR/unused-warning-point-at-identifier.rs:19:4 | LL | fn func() -> usize { - | ^^^^ + | ^^^^ help: if this is intentional, prefix it with an underscore: `_func` + | + = note: the leading underscore signals that this function serves some other purpose even if it isn't used in a way that we can detect. warning: function is never used: `func_complete_span` --> $DIR/unused-warning-point-at-identifier.rs:24:1 | LL | func_complete_span() - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_func_complete_span` + | + = note: the leading underscore signals that this function serves some other purpose even if it isn't used in a way that we can detect. warning: 4 warnings emitted diff --git a/src/test/ui/test-attrs/test-warns-dead-code.stderr b/src/test/ui/test-attrs/test-warns-dead-code.stderr index d3bcea2951364..e5a8dee6ccb68 100644 --- a/src/test/ui/test-attrs/test-warns-dead-code.stderr +++ b/src/test/ui/test-attrs/test-warns-dead-code.stderr @@ -2,8 +2,9 @@ error: function is never used: `dead` --> $DIR/test-warns-dead-code.rs:5:4 | LL | fn dead() {} - | ^^^^ + | ^^^^ help: if this is intentional, prefix it with an underscore: `_dead` | + = note: the leading underscore signals that this function serves some other purpose even if it isn't used in a way that we can detect. note: the lint level is defined here --> $DIR/test-warns-dead-code.rs:3:9 | diff --git a/src/test/ui/traits/alias/suggest-trait-alias-instead-of-type.fixed b/src/test/ui/traits/alias/suggest-trait-alias-instead-of-type.fixed new file mode 100644 index 0000000000000..8a94abaeb0744 --- /dev/null +++ b/src/test/ui/traits/alias/suggest-trait-alias-instead-of-type.fixed @@ -0,0 +1,13 @@ +// Regression test of #43913. + +// run-rustfix + +#![feature(trait_alias)] +#![allow(bare_trait_objects, dead_code)] + +trait Strings = Iterator; + +struct Struct(S); +//~^ ERROR: expected trait, found type alias `Strings` + +fn main() {} diff --git a/src/test/ui/traits/alias/suggest-trait-alias-instead-of-type.rs b/src/test/ui/traits/alias/suggest-trait-alias-instead-of-type.rs new file mode 100644 index 0000000000000..40c678c281f4c --- /dev/null +++ b/src/test/ui/traits/alias/suggest-trait-alias-instead-of-type.rs @@ -0,0 +1,13 @@ +// Regression test of #43913. + +// run-rustfix + +#![feature(trait_alias)] +#![allow(bare_trait_objects, dead_code)] + +type Strings = Iterator; + +struct Struct(S); +//~^ ERROR: expected trait, found type alias `Strings` + +fn main() {} diff --git a/src/test/ui/traits/alias/suggest-trait-alias-instead-of-type.stderr b/src/test/ui/traits/alias/suggest-trait-alias-instead-of-type.stderr new file mode 100644 index 0000000000000..6e03eeada499c --- /dev/null +++ b/src/test/ui/traits/alias/suggest-trait-alias-instead-of-type.stderr @@ -0,0 +1,14 @@ +error[E0404]: expected trait, found type alias `Strings` + --> $DIR/suggest-trait-alias-instead-of-type.rs:10:18 + | +LL | struct Struct(S); + | ^^^^^^^ type aliases cannot be used as traits + | +help: you might have meant to use `#![feature(trait_alias)]` instead of a `type` alias + | +LL | trait Strings = Iterator; + | + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0404`. diff --git a/src/test/ui/union/union-fields-1.stderr b/src/test/ui/union/union-fields-1.stderr index 87621cc01b1b9..3595bf6dce688 100644 --- a/src/test/ui/union/union-fields-1.stderr +++ b/src/test/ui/union/union-fields-1.stderr @@ -2,8 +2,11 @@ error: field is never read: `c` --> $DIR/union-fields-1.rs:6:5 | LL | c: u8, - | ^^^^^ + | -^^^^ + | | + | help: if this is intentional, prefix it with an underscore: `_c` | + = note: the leading underscore signals that this field serves some other purpose even if it isn't used in a way that we can detect. (e.g. for its effect when dropped or in foreign code) note: the lint level is defined here --> $DIR/union-fields-1.rs:1:9 | @@ -14,19 +17,31 @@ error: field is never read: `a` --> $DIR/union-fields-1.rs:9:5 | LL | a: u8, - | ^^^^^ + | -^^^^ + | | + | help: if this is intentional, prefix it with an underscore: `_a` + | + = note: the leading underscore signals that this field serves some other purpose even if it isn't used in a way that we can detect. (e.g. for its effect when dropped or in foreign code) error: field is never read: `a` --> $DIR/union-fields-1.rs:13:20 | LL | union NoDropLike { a: u8 } - | ^^^^^ + | -^^^^ + | | + | help: if this is intentional, prefix it with an underscore: `_a` + | + = note: the leading underscore signals that this field serves some other purpose even if it isn't used in a way that we can detect. (e.g. for its effect when dropped or in foreign code) error: field is never read: `c` --> $DIR/union-fields-1.rs:18:5 | LL | c: u8, - | ^^^^^ + | -^^^^ + | | + | help: if this is intentional, prefix it with an underscore: `_c` + | + = note: the leading underscore signals that this field serves some other purpose even if it isn't used in a way that we can detect. (e.g. for its effect when dropped or in foreign code) error: aborting due to 4 previous errors diff --git a/src/test/ui/union/union-lint-dead-code.stderr b/src/test/ui/union/union-lint-dead-code.stderr index 7de70ec33801f..8ead9cde9b333 100644 --- a/src/test/ui/union/union-lint-dead-code.stderr +++ b/src/test/ui/union/union-lint-dead-code.stderr @@ -2,8 +2,11 @@ error: field is never read: `b` --> $DIR/union-lint-dead-code.rs:5:5 | LL | b: bool, - | ^^^^^^^ + | -^^^^^^ + | | + | help: if this is intentional, prefix it with an underscore: `_b` | + = note: the leading underscore signals that this field serves some other purpose even if it isn't used in a way that we can detect. (e.g. for its effect when dropped or in foreign code) note: the lint level is defined here --> $DIR/union-lint-dead-code.rs:1:9 |