diff options
152 files changed, 3113 insertions, 1468 deletions
diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index a27853fa0a8..819cb5ef137 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -443,6 +443,12 @@ fn codegen_regular_intrinsic_call<'tcx>( ret.write_cvalue(fx, a); } + sym::is_val_statically_known => { + intrinsic_args!(fx, args => (_a); intrinsic); + + let res = fx.bcx.ins().iconst(types::I8, 0); + ret.write_cvalue(fx, CValue::by_val(res, ret.layout())); + } sym::breakpoint => { intrinsic_args!(fx, args => (); intrinsic); diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index 053f759329f..5760d96165d 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -196,15 +196,16 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { let mut functions = FxHashMap::default(); let builtins = [ - "__builtin_unreachable", "abort", "__builtin_expect", "__builtin_add_overflow", "__builtin_mul_overflow", - "__builtin_saddll_overflow", /*"__builtin_sadd_overflow",*/ "__builtin_smulll_overflow", /*"__builtin_smul_overflow",*/ + "__builtin_unreachable", "abort", "__builtin_expect", /*"__builtin_expect_with_probability",*/ + "__builtin_constant_p", "__builtin_add_overflow", "__builtin_mul_overflow", "__builtin_saddll_overflow", + /*"__builtin_sadd_overflow",*/ "__builtin_smulll_overflow", /*"__builtin_smul_overflow",*/ "__builtin_ssubll_overflow", /*"__builtin_ssub_overflow",*/ "__builtin_sub_overflow", "__builtin_uaddll_overflow", "__builtin_uadd_overflow", "__builtin_umulll_overflow", "__builtin_umul_overflow", "__builtin_usubll_overflow", "__builtin_usub_overflow", "sqrtf", "sqrt", "__builtin_powif", "__builtin_powi", "sinf", "sin", "cosf", "cos", "powf", "pow", "expf", "exp", "exp2f", "exp2", "logf", "log", "log10f", "log10", "log2f", "log2", "fmaf", "fma", "fabsf", "fabs", "fminf", "fmin", "fmaxf", "fmax", "copysignf", "copysign", "floorf", "floor", "ceilf", "ceil", "truncf", "trunc", "rintf", "rint", "nearbyintf", "nearbyint", "roundf", "round", - "__builtin_expect_with_probability", + ]; for builtin in builtins.iter() { diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index 85b891fce3e..eac8cb43779 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -123,6 +123,12 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { sym::unlikely => { self.expect(args[0].immediate(), false) } + sym::is_val_statically_known => { + let a = args[0].immediate(); + let builtin = self.context.get_builtin_function("__builtin_constant_p"); + let res = self.context.new_call(None, builtin, &[a]); + self.icmp(IntPredicate::IntEQ, res, self.const_i32(0)) + } kw::Try => { try_intrinsic( self, diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index f3b2caf9b80..5ef05dfbe4c 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -916,6 +916,20 @@ impl<'ll> CodegenCx<'ll, '_> { ifn!("llvm.lifetime.start.p0i8", fn(t_i64, ptr) -> void); ifn!("llvm.lifetime.end.p0i8", fn(t_i64, ptr) -> void); + // FIXME: This is an infinitesimally small portion of the types you can + // pass to this intrinsic, if we can ever lazily register intrinsics we + // should register these when they're used, that way any type can be + // passed. + ifn!("llvm.is.constant.i1", fn(i1) -> i1); + ifn!("llvm.is.constant.i8", fn(t_i8) -> i1); + ifn!("llvm.is.constant.i16", fn(t_i16) -> i1); + ifn!("llvm.is.constant.i32", fn(t_i32) -> i1); + ifn!("llvm.is.constant.i64", fn(t_i64) -> i1); + ifn!("llvm.is.constant.i128", fn(t_i128) -> i1); + ifn!("llvm.is.constant.isize", fn(t_isize) -> i1); + ifn!("llvm.is.constant.f32", fn(t_f32) -> i1); + ifn!("llvm.is.constant.f64", fn(t_f64) -> i1); + ifn!("llvm.expect.i1", fn(i1, i1) -> i1); ifn!("llvm.eh.typeid.for", fn(ptr) -> t_i32); ifn!("llvm.localescape", fn(...) -> void); diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index a0f9d5cf7cd..f1a6f7bd8e6 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -119,6 +119,10 @@ impl<'ll, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'_, 'll, 'tcx> { sym::likely => { self.call_intrinsic("llvm.expect.i1", &[args[0].immediate(), self.const_bool(true)]) } + sym::is_val_statically_known => self.call_intrinsic( + &format!("llvm.is.constant.{:?}", args[0].layout.immediate_llvm_type(self.cx)), + &[args[0].immediate()], + ), sym::unlikely => self .call_intrinsic("llvm.expect.i1", &[args[0].immediate(), self.const_bool(false)]), kw::Try => { diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index d5a33b6f01e..274ff25fb9b 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -531,6 +531,11 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, )?; } } + // The intrinsic represents whether the value is known to the optimizer (LLVM). + // We're not doing any optimizations here, so there is no optimizer that could know the value. + // (We know the value here in the machine of course, but this is the runtime of that code, + // not the optimization stage.) + sym::is_val_statically_known => ecx.write_scalar(Scalar::from_bool(false), dest)?, _ => { throw_unsup_format!( "intrinsic `{intrinsic_name}` is not supported at compile-time" diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index 4a654480ef5..064d97a49d7 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -864,9 +864,6 @@ impl<'tcx> ReportErrorExt for InvalidProgramInfo<'tcx> { InvalidProgramInfo::FnAbiAdjustForForeignAbi(_) => { rustc_middle::error::middle_adjust_for_foreign_abi_error } - InvalidProgramInfo::ConstPropNonsense => { - panic!("We had const-prop nonsense, this should never be printed") - } } } fn add_args<G: EmissionGuarantee>( @@ -875,9 +872,7 @@ impl<'tcx> ReportErrorExt for InvalidProgramInfo<'tcx> { builder: &mut DiagnosticBuilder<'_, G>, ) { match self { - InvalidProgramInfo::TooGeneric - | InvalidProgramInfo::AlreadyReported(_) - | InvalidProgramInfo::ConstPropNonsense => {} + InvalidProgramInfo::TooGeneric | InvalidProgramInfo::AlreadyReported(_) => {} InvalidProgramInfo::Layout(e) => { // The level doesn't matter, `diag` is consumed without it being used. let dummy_level = Level::Bug; diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index b39b219b46a..80d4bda4827 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -643,11 +643,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let layout = self.layout_of_local(frame, local, layout)?; let op = *frame.locals[local].access()?; if matches!(op, Operand::Immediate(_)) { - if layout.is_unsized() { - // ConstProp marks *all* locals as `Immediate::Uninit` since it cannot - // efficiently check whether they are sized. We have to catch that case here. - throw_inval!(ConstPropNonsense); - } + assert!(!layout.is_unsized()); } Ok(OpTy { op, layout }) } diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index b39efad61bb..772445f4f62 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -519,11 +519,7 @@ where } else { // Unsized `Local` isn't okay (we cannot store the metadata). match frame_ref.locals[local].access()? { - Operand::Immediate(_) => { - // ConstProp marks *all* locals as `Immediate::Uninit` since it cannot - // efficiently check whether they are sized. We have to catch that case here. - throw_inval!(ConstPropNonsense); - } + Operand::Immediate(_) => bug!(), Operand::Indirect(mplace) => Place::Ptr(*mplace), } }; @@ -816,17 +812,8 @@ where // avoid force_allocation. let src = match self.read_immediate_raw(src)? { Right(src_val) => { - // FIXME(const_prop): Const-prop can possibly evaluate an - // unsized copy operation when it thinks that the type is - // actually sized, due to a trivially false where-clause - // predicate like `where Self: Sized` with `Self = dyn Trait`. - // See #102553 for an example of such a predicate. - if src.layout().is_unsized() { - throw_inval!(ConstPropNonsense); - } - if dest.layout().is_unsized() { - throw_inval!(ConstPropNonsense); - } + assert!(!src.layout().is_unsized()); + assert!(!dest.layout().is_unsized()); assert_eq!(src.layout().size, dest.layout().size); // Yay, we got a value that we can write directly. return if layout_compat { diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs index 9a034ba22b9..bd60e066f72 100644 --- a/compiler/rustc_const_eval/src/interpret/projection.rs +++ b/compiler/rustc_const_eval/src/interpret/projection.rs @@ -153,11 +153,7 @@ where // Offset may need adjustment for unsized fields. let (meta, offset) = if field_layout.is_unsized() { - if base.layout().is_sized() { - // An unsized field of a sized type? Sure... - // But const-prop actually feeds us such nonsense MIR! (see test `const_prop/issue-86351.rs`) - throw_inval!(ConstPropNonsense); - } + assert!(!base.layout().is_sized()); let base_meta = base.meta(); // Re-use parent metadata to determine dynamic field layout. // With custom DSTS, this *will* execute user-defined code, but the same @@ -205,29 +201,26 @@ where // see https://github.com/rust-lang/rust/issues/93688#issuecomment-1032929496.) // So we just "offset" by 0. let layout = base.layout().for_variant(self, variant); - if layout.abi.is_uninhabited() { - // `read_discriminant` should have excluded uninhabited variants... but ConstProp calls - // us on dead code. - // In the future we might want to allow this to permit code like this: - // (this is a Rust/MIR pseudocode mix) - // ``` - // enum Option2 { - // Some(i32, !), - // None, - // } - // - // fn panic() -> ! { panic!() } - // - // let x: Option2; - // x.Some.0 = 42; - // x.Some.1 = panic(); - // SetDiscriminant(x, Some); - // ``` - // However, for now we don't generate such MIR, and this check here *has* found real - // bugs (see https://github.com/rust-lang/rust/issues/115145), so we will keep rejecting - // it. - throw_inval!(ConstPropNonsense) - } + // In the future we might want to allow this to permit code like this: + // (this is a Rust/MIR pseudocode mix) + // ``` + // enum Option2 { + // Some(i32, !), + // None, + // } + // + // fn panic() -> ! { panic!() } + // + // let x: Option2; + // x.Some.0 = 42; + // x.Some.1 = panic(); + // SetDiscriminant(x, Some); + // ``` + // However, for now we don't generate such MIR, and this check here *has* found real + // bugs (see https://github.com/rust-lang/rust/issues/115145), so we will keep rejecting + // it. + assert!(!layout.abi.is_uninhabited()); + // This cannot be `transmute` as variants *can* have a smaller size than the entire enum. base.offset(Size::ZERO, layout, self) } diff --git a/compiler/rustc_const_eval/src/util/mod.rs b/compiler/rustc_const_eval/src/util/mod.rs index 1e58bd645cd..a8060463b69 100644 --- a/compiler/rustc_const_eval/src/util/mod.rs +++ b/compiler/rustc_const_eval/src/util/mod.rs @@ -14,7 +14,7 @@ pub use self::type_name::type_name; /// Classify whether an operator is "left-homogeneous", i.e., the LHS has the /// same type as the result. #[inline] -pub(crate) fn binop_left_homogeneous(op: mir::BinOp) -> bool { +pub fn binop_left_homogeneous(op: mir::BinOp) -> bool { use rustc_middle::mir::BinOp::*; match op { Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Div | Rem | BitXor @@ -26,7 +26,7 @@ pub(crate) fn binop_left_homogeneous(op: mir::BinOp) -> bool { /// Classify whether an operator is "right-homogeneous", i.e., the RHS has the /// same type as the LHS. #[inline] -pub(crate) fn binop_right_homogeneous(op: mir::BinOp) -> bool { +pub fn binop_right_homogeneous(op: mir::BinOp) -> bool { use rustc_middle::mir::BinOp::*; match op { Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Div | Rem | BitXor diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 85d10872b3d..9fb318e2ae7 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -208,6 +208,10 @@ language_item_table! { FnMut, sym::fn_mut, fn_mut_trait, Target::Trait, GenericRequirement::Exact(1); FnOnce, sym::fn_once, fn_once_trait, Target::Trait, GenericRequirement::Exact(1); + AsyncFn, sym::async_fn, async_fn_trait, Target::Trait, GenericRequirement::Exact(1); + AsyncFnMut, sym::async_fn_mut, async_fn_mut_trait, Target::Trait, GenericRequirement::Exact(1); + AsyncFnOnce, sym::async_fn_once, async_fn_once_trait, Target::Trait, GenericRequirement::Exact(1); + FnOnceOutput, sym::fn_once_output, fn_once_output, Target::AssocTy, GenericRequirement::None; Iterator, sym::iterator, iterator_trait, Target::Trait, GenericRequirement::Exact(0); diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 7c3e296dfce..60e4403c1c7 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -453,6 +453,8 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { sym::black_box => (1, vec![param(0)], param(0)), + sym::is_val_statically_known => (1, vec![param(0)], tcx.types.bool), + sym::const_eval_select => (4, vec![param(0), param(1), param(2)], param(3)), sym::vtable_size | sym::vtable_align => { diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 8d362f74b0a..5a387844593 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -25,14 +25,21 @@ use rustc_trait_selection::traits::ObligationCtxt; use rustc_trait_selection::traits::{self, ObligationCause}; use std::collections::BTreeMap; -pub fn check_trait(tcx: TyCtxt<'_>, trait_def_id: DefId) { +pub fn check_trait(tcx: TyCtxt<'_>, trait_def_id: DefId) -> Result<(), ErrorGuaranteed> { let lang_items = tcx.lang_items(); - Checker { tcx, trait_def_id } - .check(lang_items.drop_trait(), visit_implementation_of_drop) - .check(lang_items.copy_trait(), visit_implementation_of_copy) - .check(lang_items.const_param_ty_trait(), visit_implementation_of_const_param_ty) - .check(lang_items.coerce_unsized_trait(), visit_implementation_of_coerce_unsized) - .check(lang_items.dispatch_from_dyn_trait(), visit_implementation_of_dispatch_from_dyn); + let checker = Checker { tcx, trait_def_id }; + let mut res = checker.check(lang_items.drop_trait(), visit_implementation_of_drop); + res = res.and(checker.check(lang_items.copy_trait(), visit_implementation_of_copy)); + res = res.and( + checker.check(lang_items.const_param_ty_trait(), visit_implementation_of_const_param_ty), + ); + res = res.and( + checker.check(lang_items.coerce_unsized_trait(), visit_implementation_of_coerce_unsized), + ); + res.and( + checker + .check(lang_items.dispatch_from_dyn_trait(), visit_implementation_of_dispatch_from_dyn), + ) } struct Checker<'tcx> { @@ -41,33 +48,40 @@ struct Checker<'tcx> { } impl<'tcx> Checker<'tcx> { - fn check<F>(&self, trait_def_id: Option<DefId>, mut f: F) -> &Self + fn check<F>(&self, trait_def_id: Option<DefId>, mut f: F) -> Result<(), ErrorGuaranteed> where - F: FnMut(TyCtxt<'tcx>, LocalDefId), + F: FnMut(TyCtxt<'tcx>, LocalDefId) -> Result<(), ErrorGuaranteed>, { + let mut res = Ok(()); if Some(self.trait_def_id) == trait_def_id { for &impl_def_id in self.tcx.hir().trait_impls(self.trait_def_id) { - f(self.tcx, impl_def_id); + res = res.and(f(self.tcx, impl_def_id)); } } - self + res } } -fn visit_implementation_of_drop(tcx: TyCtxt<'_>, impl_did: LocalDefId) { +fn visit_implementation_of_drop( + tcx: TyCtxt<'_>, + impl_did: LocalDefId, +) -> Result<(), ErrorGuaranteed> { // Destructors only work on local ADT types. match tcx.type_of(impl_did).instantiate_identity().kind() { - ty::Adt(def, _) if def.did().is_local() => return, - ty::Error(_) => return, + ty::Adt(def, _) if def.did().is_local() => return Ok(()), + ty::Error(_) => return Ok(()), _ => {} } let impl_ = tcx.hir().expect_item(impl_did).expect_impl(); - tcx.dcx().emit_err(errors::DropImplOnWrongItem { span: impl_.self_ty.span }); + Err(tcx.dcx().emit_err(errors::DropImplOnWrongItem { span: impl_.self_ty.span })) } -fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) { +fn visit_implementation_of_copy( + tcx: TyCtxt<'_>, + impl_did: LocalDefId, +) -> Result<(), ErrorGuaranteed> { debug!("visit_implementation_of_copy: impl_did={:?}", impl_did); let self_type = tcx.type_of(impl_did).instantiate_identity(); @@ -79,59 +93,68 @@ fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) { debug!("visit_implementation_of_copy: self_type={:?} (free)", self_type); let span = match tcx.hir().expect_item(impl_did).expect_impl() { - hir::Impl { polarity: hir::ImplPolarity::Negative(_), .. } => return, + hir::Impl { polarity: hir::ImplPolarity::Negative(_), .. } => return Ok(()), hir::Impl { self_ty, .. } => self_ty.span, }; let cause = traits::ObligationCause::misc(span, impl_did); match type_allowed_to_implement_copy(tcx, param_env, self_type, cause) { - Ok(()) => {} + Ok(()) => Ok(()), Err(CopyImplementationError::InfringingFields(fields)) => { - infringing_fields_error(tcx, fields, LangItem::Copy, impl_did, span); + Err(infringing_fields_error(tcx, fields, LangItem::Copy, impl_did, span)) } Err(CopyImplementationError::NotAnAdt) => { - tcx.dcx().emit_err(errors::CopyImplOnNonAdt { span }); + Err(tcx.dcx().emit_err(errors::CopyImplOnNonAdt { span })) } Err(CopyImplementationError::HasDestructor) => { - tcx.dcx().emit_err(errors::CopyImplOnTypeWithDtor { span }); + Err(tcx.dcx().emit_err(errors::CopyImplOnTypeWithDtor { span })) } } } -fn visit_implementation_of_const_param_ty(tcx: TyCtxt<'_>, impl_did: LocalDefId) { +fn visit_implementation_of_const_param_ty( + tcx: TyCtxt<'_>, + impl_did: LocalDefId, +) -> Result<(), ErrorGuaranteed> { let self_type = tcx.type_of(impl_did).instantiate_identity(); assert!(!self_type.has_escaping_bound_vars()); let param_env = tcx.param_env(impl_did); let span = match tcx.hir().expect_item(impl_did).expect_impl() { - hir::Impl { polarity: hir::ImplPolarity::Negative(_), .. } => return, + hir::Impl { polarity: hir::ImplPolarity::Negative(_), .. } => return Ok(()), impl_ => impl_.self_ty.span, }; let cause = traits::ObligationCause::misc(span, impl_did); match type_allowed_to_implement_const_param_ty(tcx, param_env, self_type, cause) { - Ok(()) => {} + Ok(()) => Ok(()), Err(ConstParamTyImplementationError::InfrigingFields(fields)) => { - infringing_fields_error(tcx, fields, LangItem::ConstParamTy, impl_did, span); + Err(infringing_fields_error(tcx, fields, LangItem::ConstParamTy, impl_did, span)) } Err(ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed) => { - tcx.dcx().emit_err(errors::ConstParamTyImplOnNonAdt { span }); + Err(tcx.dcx().emit_err(errors::ConstParamTyImplOnNonAdt { span })) } } } -fn visit_implementation_of_coerce_unsized(tcx: TyCtxt<'_>, impl_did: LocalDefId) { +fn visit_implementation_of_coerce_unsized( + tcx: TyCtxt<'_>, + impl_did: LocalDefId, +) -> Result<(), ErrorGuaranteed> { debug!("visit_implementation_of_coerce_unsized: impl_did={:?}", impl_did); // Just compute this for the side-effects, in particular reporting // errors; other parts of the code may demand it for the info of // course. let span = tcx.def_span(impl_did); - tcx.at(span).coerce_unsized_info(impl_did); + tcx.at(span).ensure().coerce_unsized_info(impl_did) } -fn visit_implementation_of_dispatch_from_dyn(tcx: TyCtxt<'_>, impl_did: LocalDefId) { +fn visit_implementation_of_dispatch_from_dyn( + tcx: TyCtxt<'_>, + impl_did: LocalDefId, +) -> Result<(), ErrorGuaranteed> { debug!("visit_implementation_of_dispatch_from_dyn: impl_did={:?}", impl_did); let span = tcx.def_span(impl_did); @@ -166,26 +189,28 @@ fn visit_implementation_of_dispatch_from_dyn(tcx: TyCtxt<'_>, impl_did: LocalDef match (source.kind(), target.kind()) { (&Ref(r_a, _, mutbl_a), Ref(r_b, _, mutbl_b)) if infcx.at(&cause, param_env).eq(DefineOpaqueTypes::No, r_a, *r_b).is_ok() - && mutbl_a == *mutbl_b => {} - (&RawPtr(tm_a), &RawPtr(tm_b)) if tm_a.mutbl == tm_b.mutbl => (), + && mutbl_a == *mutbl_b => + { + Ok(()) + } + (&RawPtr(tm_a), &RawPtr(tm_b)) if tm_a.mutbl == tm_b.mutbl => Ok(()), (&Adt(def_a, args_a), &Adt(def_b, args_b)) if def_a.is_struct() && def_b.is_struct() => { if def_a != def_b { let source_path = tcx.def_path_str(def_a.did()); let target_path = tcx.def_path_str(def_b.did()); - tcx.dcx().emit_err(errors::DispatchFromDynCoercion { + return Err(tcx.dcx().emit_err(errors::DispatchFromDynCoercion { span, trait_name: "DispatchFromDyn", note: true, source_path, target_path, - }); - - return; + })); } + let mut res = Ok(()); if def_a.repr().c() || def_a.repr().packed() { - tcx.dcx().emit_err(errors::DispatchFromDynRepr { span }); + res = Err(tcx.dcx().emit_err(errors::DispatchFromDynRepr { span })); } let fields = &def_a.non_enum_variant().fields; @@ -207,11 +232,11 @@ fn visit_implementation_of_dispatch_from_dyn(tcx: TyCtxt<'_>, impl_did: LocalDef infcx.at(&cause, param_env).eq(DefineOpaqueTypes::No, ty_a, ty_b) { if ok.obligations.is_empty() { - tcx.dcx().emit_err(errors::DispatchFromDynZST { + res = Err(tcx.dcx().emit_err(errors::DispatchFromDynZST { span, name: field.name, ty: ty_a, - }); + })); return false; } @@ -222,13 +247,13 @@ fn visit_implementation_of_dispatch_from_dyn(tcx: TyCtxt<'_>, impl_did: LocalDef .collect::<Vec<_>>(); if coerced_fields.is_empty() { - tcx.dcx().emit_err(errors::DispatchFromDynSingle { + res = Err(tcx.dcx().emit_err(errors::DispatchFromDynSingle { span, trait_name: "DispatchFromDyn", note: true, - }); + })); } else if coerced_fields.len() > 1 { - tcx.dcx().emit_err(errors::DispatchFromDynMulti { + res = Err(tcx.dcx().emit_err(errors::DispatchFromDynMulti { span, coercions_note: true, number: coerced_fields.len(), @@ -244,7 +269,7 @@ fn visit_implementation_of_dispatch_from_dyn(tcx: TyCtxt<'_>, impl_did: LocalDef }) .collect::<Vec<_>>() .join(", "), - }); + })); } else { let ocx = ObligationCtxt::new(&infcx); for field in coerced_fields { @@ -261,21 +286,25 @@ fn visit_implementation_of_dispatch_from_dyn(tcx: TyCtxt<'_>, impl_did: LocalDef } let errors = ocx.select_all_or_error(); if !errors.is_empty() { - infcx.err_ctxt().report_fulfillment_errors(errors); + res = Err(infcx.err_ctxt().report_fulfillment_errors(errors)); } // Finally, resolve all regions. let outlives_env = OutlivesEnvironment::new(param_env); - let _ = ocx.resolve_regions_and_report_errors(impl_did, &outlives_env); + res = res.and(ocx.resolve_regions_and_report_errors(impl_did, &outlives_env)); } + res } - _ => { - tcx.dcx().emit_err(errors::CoerceUnsizedMay { span, trait_name: "DispatchFromDyn" }); - } + _ => Err(tcx + .dcx() + .emit_err(errors::CoerceUnsizedMay { span, trait_name: "DispatchFromDyn" })), } } -pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: LocalDefId) -> CoerceUnsizedInfo { +pub fn coerce_unsized_info<'tcx>( + tcx: TyCtxt<'tcx>, + impl_did: LocalDefId, +) -> Result<CoerceUnsizedInfo, ErrorGuaranteed> { debug!("compute_coerce_unsized_info(impl_did={:?})", impl_did); let span = tcx.def_span(impl_did); @@ -292,8 +321,6 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: LocalDefId) -> Coe let param_env = tcx.param_env(impl_did); assert!(!source.has_escaping_bound_vars()); - let err_info = CoerceUnsizedInfo { custom_kind: None }; - debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (free)", source, target); let infcx = tcx.infer_ctxt().build(); @@ -337,14 +364,13 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: LocalDefId) -> Coe if def_a != def_b { let source_path = tcx.def_path_str(def_a.did()); let target_path = tcx.def_path_str(def_b.did()); - tcx.dcx().emit_err(errors::DispatchFromDynSame { + return Err(tcx.dcx().emit_err(errors::DispatchFromDynSame { span, trait_name: "CoerceUnsized", note: true, source_path, target_path, - }); - return err_info; + })); } // Here we are considering a case of converting @@ -419,12 +445,11 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: LocalDefId) -> Coe .collect::<Vec<_>>(); if diff_fields.is_empty() { - tcx.dcx().emit_err(errors::CoerceUnsizedOneField { + return Err(tcx.dcx().emit_err(errors::CoerceUnsizedOneField { span, trait_name: "CoerceUnsized", note: true, - }); - return err_info; + })); } else if diff_fields.len() > 1 { let item = tcx.hir().expect_item(impl_did); let span = if let ItemKind::Impl(hir::Impl { of_trait: Some(t), .. }) = &item.kind { @@ -433,7 +458,7 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: LocalDefId) -> Coe tcx.def_span(impl_did) }; - tcx.dcx().emit_err(errors::CoerceUnsizedMulti { + return Err(tcx.dcx().emit_err(errors::CoerceUnsizedMulti { span, coercions_note: true, number: diff_fields.len(), @@ -442,9 +467,7 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: LocalDefId) -> Coe .map(|&(i, a, b)| format!("`{}` (`{}` to `{}`)", fields[i].name, a, b)) .collect::<Vec<_>>() .join(", "), - }); - - return err_info; + })); } let (i, a, b) = diff_fields[0]; @@ -453,8 +476,9 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: LocalDefId) -> Coe } _ => { - tcx.dcx().emit_err(errors::DispatchFromDynStruct { span, trait_name: "CoerceUnsized" }); - return err_info; + return Err(tcx + .dcx() + .emit_err(errors::DispatchFromDynStruct { span, trait_name: "CoerceUnsized" })); } }; @@ -477,7 +501,7 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: LocalDefId) -> Coe let outlives_env = OutlivesEnvironment::new(param_env); let _ = ocx.resolve_regions_and_report_errors(impl_did, &outlives_env); - CoerceUnsizedInfo { custom_kind: kind } + Ok(CoerceUnsizedInfo { custom_kind: kind }) } fn infringing_fields_error( diff --git a/compiler/rustc_hir_analysis/src/coherence/mod.rs b/compiler/rustc_hir_analysis/src/coherence/mod.rs index 561a254e89e..dafa899ef24 100644 --- a/compiler/rustc_hir_analysis/src/coherence/mod.rs +++ b/compiler/rustc_hir_analysis/src/coherence/mod.rs @@ -10,6 +10,7 @@ use rustc_errors::{error_code, struct_span_code_err}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::query::Providers; use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; +use rustc_span::ErrorGuaranteed; use rustc_trait_selection::traits; mod builtin; @@ -18,7 +19,11 @@ mod inherent_impls_overlap; mod orphan; mod unsafety; -fn check_impl(tcx: TyCtxt<'_>, impl_def_id: LocalDefId, trait_ref: ty::TraitRef<'_>) { +fn check_impl( + tcx: TyCtxt<'_>, + impl_def_id: LocalDefId, + trait_ref: ty::TraitRef<'_>, +) -> Result<(), ErrorGuaranteed> { debug!( "(checking implementation) adding impl for trait '{:?}', item '{}'", trait_ref, @@ -28,18 +33,18 @@ fn check_impl(tcx: TyCtxt<'_>, impl_def_id: LocalDefId, trait_ref: ty::TraitRef< // Skip impls where one of the self type is an error type. // This occurs with e.g., resolve failures (#30589). if trait_ref.references_error() { - return; + return Ok(()); } - enforce_trait_manually_implementable(tcx, impl_def_id, trait_ref.def_id); - enforce_empty_impls_for_marker_traits(tcx, impl_def_id, trait_ref.def_id); + enforce_trait_manually_implementable(tcx, impl_def_id, trait_ref.def_id) + .and(enforce_empty_impls_for_marker_traits(tcx, impl_def_id, trait_ref.def_id)) } fn enforce_trait_manually_implementable( tcx: TyCtxt<'_>, impl_def_id: LocalDefId, trait_def_id: DefId, -) { +) -> Result<(), ErrorGuaranteed> { let impl_header_span = tcx.def_span(impl_def_id); // Disallow *all* explicit impls of traits marked `#[rustc_deny_explicit_impl]` @@ -59,18 +64,17 @@ fn enforce_trait_manually_implementable( err.code(error_code!(E0328)); } - err.emit(); - return; + return Err(err.emit()); } if let ty::trait_def::TraitSpecializationKind::AlwaysApplicable = tcx.trait_def(trait_def_id).specialization_kind { if !tcx.features().specialization && !tcx.features().min_specialization { - tcx.dcx().emit_err(errors::SpecializationTrait { span: impl_header_span }); - return; + return Err(tcx.dcx().emit_err(errors::SpecializationTrait { span: impl_header_span })); } } + Ok(()) } /// We allow impls of marker traits to overlap, so they can't override impls @@ -79,22 +83,22 @@ fn enforce_empty_impls_for_marker_traits( tcx: TyCtxt<'_>, impl_def_id: LocalDefId, trait_def_id: DefId, -) { +) -> Result<(), ErrorGuaranteed> { if !tcx.trait_def(trait_def_id).is_marker { - return; + return Ok(()); } if tcx.associated_item_def_ids(trait_def_id).is_empty() { - return; + return Ok(()); } - struct_span_code_err!( + Err(struct_span_code_err!( tcx.dcx(), tcx.def_span(impl_def_id), E0715, "impls for marker traits cannot contain items" ) - .emit(); + .emit()) } pub fn provide(providers: &mut Providers) { @@ -115,23 +119,23 @@ pub fn provide(providers: &mut Providers) { }; } -fn coherent_trait(tcx: TyCtxt<'_>, def_id: DefId) { +fn coherent_trait(tcx: TyCtxt<'_>, def_id: DefId) -> Result<(), ErrorGuaranteed> { // Trigger building the specialization graph for the trait. This will detect and report any // overlap errors. - tcx.ensure().specialization_graph_of(def_id); + let mut res = tcx.ensure().specialization_graph_of(def_id); let impls = tcx.hir().trait_impls(def_id); for &impl_def_id in impls { let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().instantiate_identity(); - check_impl(tcx, impl_def_id, trait_ref); - check_object_overlap(tcx, impl_def_id, trait_ref); + res = res.and(check_impl(tcx, impl_def_id, trait_ref)); + res = res.and(check_object_overlap(tcx, impl_def_id, trait_ref)); - unsafety::check_item(tcx, impl_def_id); - tcx.ensure().orphan_check_impl(impl_def_id); + res = res.and(unsafety::check_item(tcx, impl_def_id)); + res = res.and(tcx.ensure().orphan_check_impl(impl_def_id)); } - builtin::check_trait(tcx, def_id); + res.and(builtin::check_trait(tcx, def_id)) } /// Checks whether an impl overlaps with the automatic `impl Trait for dyn Trait`. @@ -139,12 +143,12 @@ fn check_object_overlap<'tcx>( tcx: TyCtxt<'tcx>, impl_def_id: LocalDefId, trait_ref: ty::TraitRef<'tcx>, -) { +) -> Result<(), ErrorGuaranteed> { let trait_def_id = trait_ref.def_id; if trait_ref.references_error() { debug!("coherence: skipping impl {:?} with error {:?}", impl_def_id, trait_ref); - return; + return Ok(()); } // check for overlap with the automatic `impl Trait for dyn Trait` @@ -173,7 +177,7 @@ fn check_object_overlap<'tcx>( let mut supertrait_def_ids = traits::supertrait_def_ids(tcx, component_def_id); if supertrait_def_ids.any(|d| d == trait_def_id) { let span = tcx.def_span(impl_def_id); - struct_span_code_err!( + return Err(struct_span_code_err!( tcx.dcx(), span, E0371, @@ -189,9 +193,10 @@ fn check_object_overlap<'tcx>( tcx.def_path_str(trait_def_id) ), ) - .emit(); + .emit()); } } } } + Ok(()) } diff --git a/compiler/rustc_hir_analysis/src/coherence/unsafety.rs b/compiler/rustc_hir_analysis/src/coherence/unsafety.rs index 7b146573a1b..e4c407af53f 100644 --- a/compiler/rustc_hir_analysis/src/coherence/unsafety.rs +++ b/compiler/rustc_hir_analysis/src/coherence/unsafety.rs @@ -6,8 +6,9 @@ use rustc_hir as hir; use rustc_hir::Unsafety; use rustc_middle::ty::TyCtxt; use rustc_span::def_id::LocalDefId; +use rustc_span::ErrorGuaranteed; -pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { +pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorGuaranteed> { let item = tcx.hir().expect_item(def_id); let impl_ = item.expect_impl(); @@ -18,7 +19,7 @@ pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { impl_.generics.params.iter().find(|p| p.pure_wrt_drop).map(|_| "may_dangle"); match (trait_def.unsafety, unsafe_attr, impl_.unsafety, impl_.polarity) { (Unsafety::Normal, None, Unsafety::Unsafe, hir::ImplPolarity::Positive) => { - struct_span_code_err!( + return Err(struct_span_code_err!( tcx.dcx(), tcx.def_span(def_id), E0199, @@ -31,11 +32,11 @@ pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { "", rustc_errors::Applicability::MachineApplicable, ) - .emit(); + .emit()); } (Unsafety::Unsafe, _, Unsafety::Normal, hir::ImplPolarity::Positive) => { - struct_span_code_err!( + return Err(struct_span_code_err!( tcx.dcx(), tcx.def_span(def_id), E0200, @@ -54,11 +55,11 @@ pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { "unsafe ", rustc_errors::Applicability::MaybeIncorrect, ) - .emit(); + .emit()); } (Unsafety::Normal, Some(attr_name), Unsafety::Normal, hir::ImplPolarity::Positive) => { - struct_span_code_err!( + return Err(struct_span_code_err!( tcx.dcx(), tcx.def_span(def_id), E0569, @@ -77,7 +78,7 @@ pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { "unsafe ", rustc_errors::Applicability::MaybeIncorrect, ) - .emit(); + .emit()); } (_, _, Unsafety::Unsafe, hir::ImplPolarity::Negative(_)) => { @@ -92,4 +93,5 @@ pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { } } } + Ok(()) } diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 0a3a71ba1a2..ad5b6664677 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -172,19 +172,15 @@ pub fn check_crate(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> { tcx.sess.time("coherence_checking", || { // Check impls constrain their parameters - let res = + let mut res = tcx.hir().try_par_for_each_module(|module| tcx.ensure().check_mod_impl_wf(module)); - // FIXME(matthewjasper) We shouldn't need to use `track_errors` anywhere in this function - // or the compiler in general. - res.and(tcx.sess.track_errors(|| { - for &trait_def_id in tcx.all_local_trait_impls(()).keys() { - tcx.ensure().coherent_trait(trait_def_id); - } - })) + for &trait_def_id in tcx.all_local_trait_impls(()).keys() { + res = res.and(tcx.ensure().coherent_trait(trait_def_id)); + } // these queries are executed for side-effects (error reporting): - .and(tcx.ensure().crate_inherent_impls(())) - .and(tcx.ensure().crate_inherent_impls_overlap_check(())) + res.and(tcx.ensure().crate_inherent_impls(())) + .and(tcx.ensure().crate_inherent_impls_overlap_check(())) })?; if tcx.features().rustc_attrs { diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index d486f069989..9dc365526d4 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -220,6 +220,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (self.tcx.lang_items().fn_trait(), Ident::with_dummy_span(sym::call), true), (self.tcx.lang_items().fn_mut_trait(), Ident::with_dummy_span(sym::call_mut), true), (self.tcx.lang_items().fn_once_trait(), Ident::with_dummy_span(sym::call_once), false), + (self.tcx.lang_items().async_fn_trait(), Ident::with_dummy_span(sym::async_call), true), + ( + self.tcx.lang_items().async_fn_mut_trait(), + Ident::with_dummy_span(sym::async_call_mut), + true, + ), + ( + self.tcx.lang_items().async_fn_once_trait(), + Ident::with_dummy_span(sym::async_call_once), + false, + ), ] { let Some(trait_def_id) = opt_trait_def_id else { continue }; diff --git a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs index 01cd3c57925..21ea48c6c83 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs @@ -294,6 +294,75 @@ impl<T> Trait<T> for X { ); } } + (ty::Dynamic(t, _, ty::DynKind::Dyn), ty::Alias(ty::Opaque, alias)) + if let Some(def_id) = t.principal_def_id() + && tcx.explicit_item_bounds(alias.def_id).skip_binder().iter().any( + |(pred, _span)| match pred.kind().skip_binder() { + ty::ClauseKind::Trait(trait_predicate) + if trait_predicate.polarity + == ty::ImplPolarity::Positive => + { + trait_predicate.def_id() == def_id + } + _ => false, + }, + ) => + { + diag.help(format!( + "you can box the `{}` to coerce it to `Box<{}>`, but you'll have to \ + change the expected type as well", + values.found, values.expected, + )); + } + (ty::Dynamic(t, _, ty::DynKind::Dyn), _) + if let Some(def_id) = t.principal_def_id() => + { + let mut impl_def_ids = vec![]; + tcx.for_each_relevant_impl(def_id, values.found, |did| { + impl_def_ids.push(did) + }); + if let [_] = &impl_def_ids[..] { + let trait_name = tcx.item_name(def_id); + diag.help(format!( + "`{}` implements `{trait_name}` so you could box the found value \ + and coerce it to the trait object `Box<dyn {trait_name}>`, you \ + will have to change the expected type as well", + values.found, + )); + } + } + (_, ty::Dynamic(t, _, ty::DynKind::Dyn)) + if let Some(def_id) = t.principal_def_id() => + { + let mut impl_def_ids = vec![]; + tcx.for_each_relevant_impl(def_id, values.expected, |did| { + impl_def_ids.push(did) + }); + if let [_] = &impl_def_ids[..] { + let trait_name = tcx.item_name(def_id); + diag.help(format!( + "`{}` implements `{trait_name}` so you could change the expected \ + type to `Box<dyn {trait_name}>`", + values.expected, + )); + } + } + (ty::Dynamic(t, _, ty::DynKind::DynStar), _) + if let Some(def_id) = t.principal_def_id() => + { + let mut impl_def_ids = vec![]; + tcx.for_each_relevant_impl(def_id, values.found, |did| { + impl_def_ids.push(did) + }); + if let [_] = &impl_def_ids[..] { + let trait_name = tcx.item_name(def_id); + diag.help(format!( + "`{}` implements `{trait_name}`, `#[feature(dyn_star)]` is likely \ + not enabled; that feature it is currently incomplete", + values.found, + )); + } + } (_, ty::Alias(ty::Opaque, opaque_ty)) | (ty::Alias(ty::Opaque, opaque_ty), _) => { if opaque_ty.def_id.is_local() diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index ad1980136f3..25675e06e38 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs @@ -120,7 +120,8 @@ struct QueryModifiers { /// Forward the result on ensure if the query gets recomputed, and /// return `Ok(())` otherwise. Only applicable to queries returning - /// `Result<(), ErrorGuaranteed>` + /// `Result<T, ErrorGuaranteed>`. The `T` is not returned from `ensure` + /// invocations. ensure_forwards_result_if_red: Option<Ident>, } diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index c3d6c21c402..0b352a02b64 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -236,7 +236,14 @@ provide! { tcx, def_id, other, cdata, impl_polarity => { table_direct } defaultness => { table_direct } constness => { table_direct } - coerce_unsized_info => { table } + coerce_unsized_info => { + Ok(cdata + .root + .tables + .coerce_unsized_info + .get(cdata, def_id.index) + .map(|lazy| lazy.decode((cdata, tcx))) + .process_decoded(tcx, || panic!("{def_id:?} does not have coerce_unsized_info"))) } mir_const_qualif => { table } rendered_const => { table } asyncness => { table_direct } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 2d4e49e27d9..542caf86223 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1994,7 +1994,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // if this is an impl of `CoerceUnsized`, create its // "unsized info", else just store None if Some(trait_ref.def_id) == tcx.lang_items().coerce_unsized_trait() { - let coerce_unsized_info = tcx.coerce_unsized_info(def_id); + let coerce_unsized_info = tcx.coerce_unsized_info(def_id).unwrap(); record!(self.tables.coerce_unsized_info[def_id] <- coerce_unsized_info); } } diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 1c9b4e558f0..7a1df495451 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -200,8 +200,6 @@ pub enum InvalidProgramInfo<'tcx> { /// (which unfortunately typeck does not reject). /// Not using `FnAbiError` as that contains a nested `LayoutError`. FnAbiAdjustForForeignAbi(call::AdjustForForeignAbiError), - /// We are runnning into a nonsense situation due to ConstProp violating our invariants. - ConstPropNonsense, } /// Details of why a pointer had to be in-bounds. diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index 778b4308d2e..1b01df6a187 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -1,6 +1,7 @@ use crate::mir; use crate::query::CyclePlaceholder; use crate::traits; +use crate::ty::adjustment::CoerceUnsizedInfo; use crate::ty::{self, Ty}; use std::intrinsics::transmute_unchecked; use std::mem::{size_of, MaybeUninit}; @@ -105,6 +106,10 @@ impl EraseType for Result<Option<ty::Instance<'_>>, rustc_errors::ErrorGuarantee [u8; size_of::<Result<Option<ty::Instance<'static>>, rustc_errors::ErrorGuaranteed>>()]; } +impl EraseType for Result<CoerceUnsizedInfo, rustc_errors::ErrorGuaranteed> { + type Result = [u8; size_of::<Result<CoerceUnsizedInfo, rustc_errors::ErrorGuaranteed>>()]; +} + impl EraseType for Result<Option<ty::EarlyBinder<ty::Const<'_>>>, rustc_errors::ErrorGuaranteed> { type Result = [u8; size_of::< Result<Option<ty::EarlyBinder<ty::Const<'static>>>, rustc_errors::ErrorGuaranteed>, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 23daefd5a65..2f7ae68c692 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -977,10 +977,11 @@ rustc_queries! { } /// Caches `CoerceUnsized` kinds for impls on custom types. - query coerce_unsized_info(key: DefId) -> ty::adjustment::CoerceUnsizedInfo { + query coerce_unsized_info(key: DefId) -> Result<ty::adjustment::CoerceUnsizedInfo, ErrorGuaranteed> { desc { |tcx| "computing CoerceUnsized info for `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } separate_provide_extern + ensure_forwards_result_if_red } query typeck(key: LocalDefId) -> &'tcx ty::TypeckResults<'tcx> { @@ -1000,8 +1001,9 @@ rustc_queries! { desc { |tcx| "checking whether `{}` has a body", tcx.def_path_str(def_id) } } - query coherent_trait(def_id: DefId) -> () { + query coherent_trait(def_id: DefId) -> Result<(), ErrorGuaranteed> { desc { |tcx| "coherence checking all impls of trait `{}`", tcx.def_path_str(def_id) } + ensure_forwards_result_if_red } /// Borrow-checks the function body. If this is a closure, returns @@ -1032,6 +1034,7 @@ rustc_queries! { "checking whether impl `{}` follows the orphan rules", tcx.def_path_str(key), } + ensure_forwards_result_if_red } /// Check whether the function has any recursion that could cause the inliner to trigger @@ -1300,6 +1303,7 @@ rustc_queries! { query specialization_graph_of(trait_id: DefId) -> Result<&'tcx specialization_graph::Graph, ErrorGuaranteed> { desc { |tcx| "building specialization graph of trait `{}`", tcx.def_path_str(trait_id) } cache_on_disk_if { true } + ensure_forwards_result_if_red } query object_safety_violations(trait_id: DefId) -> &'tcx [ObjectSafetyViolation] { desc { |tcx| "determining object safety of trait `{}`", tcx.def_path_str(trait_id) } diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index ef621f29bfd..1f81678d6a5 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -350,7 +350,7 @@ impl<'tcx> TyCtxt<'tcx> { validate: impl Fn(Self, DefId) -> Result<(), ErrorGuaranteed>, ) -> Option<ty::Destructor> { let drop_trait = self.lang_items().drop_trait()?; - self.ensure().coherent_trait(drop_trait); + self.ensure().coherent_trait(drop_trait).ok()?; let ty = self.type_of(adt_did).instantiate_identity(); let mut dtor_candidate = None; diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs index c5824c30770..8b9e507c8c7 100644 --- a/compiler/rustc_mir_transform/src/const_prop.rs +++ b/compiler/rustc_mir_transform/src/const_prop.rs @@ -1,21 +1,12 @@ //! Propagates constants for early reporting of statically known //! assertion failures -use rustc_const_eval::interpret::{ - self, compile_time_machine, AllocId, ConstAllocation, FnArg, Frame, ImmTy, InterpCx, - InterpResult, OpTy, PlaceTy, Pointer, -}; -use rustc_data_structures::fx::FxHashSet; use rustc_index::bit_set::BitSet; use rustc_index::IndexVec; use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; -use rustc_middle::query::TyCtxtAt; -use rustc_middle::ty::layout::TyAndLayout; -use rustc_middle::ty::{self, ParamEnv, TyCtxt}; -use rustc_span::def_id::DefId; +use rustc_middle::ty::{ParamEnv, TyCtxt}; use rustc_target::abi::Size; -use rustc_target::spec::abi::Abi as CallAbi; /// The maximum number of bytes that we'll allocate space for a local or the return value. /// Needed for #66397, because otherwise we eval into large places and that can cause OOM or just @@ -49,162 +40,6 @@ pub(crate) macro throw_machine_stop_str($($tt:tt)*) {{ throw_machine_stop!(Zst) }} -pub(crate) struct ConstPropMachine<'mir, 'tcx> { - /// The virtual call stack. - stack: Vec<Frame<'mir, 'tcx>>, - pub written_only_inside_own_block_locals: FxHashSet<Local>, - pub can_const_prop: IndexVec<Local, ConstPropMode>, -} - -impl ConstPropMachine<'_, '_> { - pub fn new(can_const_prop: IndexVec<Local, ConstPropMode>) -> Self { - Self { - stack: Vec::new(), - written_only_inside_own_block_locals: Default::default(), - can_const_prop, - } - } -} - -impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx> { - compile_time_machine!(<'mir, 'tcx>); - - const PANIC_ON_ALLOC_FAIL: bool = true; // all allocations are small (see `MAX_ALLOC_LIMIT`) - - const POST_MONO_CHECKS: bool = false; // this MIR is still generic! - - type MemoryKind = !; - - #[inline(always)] - fn enforce_alignment(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool { - false // no reason to enforce alignment - } - - #[inline(always)] - fn enforce_validity(_ecx: &InterpCx<'mir, 'tcx, Self>, _layout: TyAndLayout<'tcx>) -> bool { - false // for now, we don't enforce validity - } - - fn load_mir( - _ecx: &InterpCx<'mir, 'tcx, Self>, - _instance: ty::InstanceDef<'tcx>, - ) -> InterpResult<'tcx, &'tcx Body<'tcx>> { - throw_machine_stop_str!("calling functions isn't supported in ConstProp") - } - - fn panic_nounwind(_ecx: &mut InterpCx<'mir, 'tcx, Self>, _msg: &str) -> InterpResult<'tcx> { - throw_machine_stop_str!("panicking isn't supported in ConstProp") - } - - fn find_mir_or_eval_fn( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, - _instance: ty::Instance<'tcx>, - _abi: CallAbi, - _args: &[FnArg<'tcx>], - _destination: &PlaceTy<'tcx>, - _target: Option<BasicBlock>, - _unwind: UnwindAction, - ) -> InterpResult<'tcx, Option<(&'mir Body<'tcx>, ty::Instance<'tcx>)>> { - Ok(None) - } - - fn call_intrinsic( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, - _instance: ty::Instance<'tcx>, - _args: &[OpTy<'tcx>], - _destination: &PlaceTy<'tcx>, - _target: Option<BasicBlock>, - _unwind: UnwindAction, - ) -> InterpResult<'tcx> { - throw_machine_stop_str!("calling intrinsics isn't supported in ConstProp") - } - - fn assert_panic( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, - _msg: &rustc_middle::mir::AssertMessage<'tcx>, - _unwind: rustc_middle::mir::UnwindAction, - ) -> InterpResult<'tcx> { - bug!("panics terminators are not evaluated in ConstProp") - } - - fn binary_ptr_op( - _ecx: &InterpCx<'mir, 'tcx, Self>, - _bin_op: BinOp, - _left: &ImmTy<'tcx>, - _right: &ImmTy<'tcx>, - ) -> InterpResult<'tcx, (ImmTy<'tcx>, bool)> { - // We can't do this because aliasing of memory can differ between const eval and llvm - throw_machine_stop_str!("pointer arithmetic or comparisons aren't supported in ConstProp") - } - - fn before_access_local_mut<'a>( - ecx: &'a mut InterpCx<'mir, 'tcx, Self>, - frame: usize, - local: Local, - ) -> InterpResult<'tcx> { - assert_eq!(frame, 0); - match ecx.machine.can_const_prop[local] { - ConstPropMode::NoPropagation => { - throw_machine_stop_str!( - "tried to write to a local that is marked as not propagatable" - ) - } - ConstPropMode::OnlyInsideOwnBlock => { - ecx.machine.written_only_inside_own_block_locals.insert(local); - } - ConstPropMode::FullConstProp => {} - } - Ok(()) - } - - fn before_access_global( - _tcx: TyCtxtAt<'tcx>, - _machine: &Self, - _alloc_id: AllocId, - alloc: ConstAllocation<'tcx>, - _static_def_id: Option<DefId>, - is_write: bool, - ) -> InterpResult<'tcx> { - if is_write { - throw_machine_stop_str!("can't write to global"); - } - // If the static allocation is mutable, then we can't const prop it as its content - // might be different at runtime. - if alloc.inner().mutability.is_mut() { - throw_machine_stop_str!("can't access mutable globals in ConstProp"); - } - - Ok(()) - } - - #[inline(always)] - fn expose_ptr(_ecx: &mut InterpCx<'mir, 'tcx, Self>, _ptr: Pointer) -> InterpResult<'tcx> { - throw_machine_stop_str!("exposing pointers isn't supported in ConstProp") - } - - #[inline(always)] - fn init_frame_extra( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, - frame: Frame<'mir, 'tcx>, - ) -> InterpResult<'tcx, Frame<'mir, 'tcx>> { - Ok(frame) - } - - #[inline(always)] - fn stack<'a>( - ecx: &'a InterpCx<'mir, 'tcx, Self>, - ) -> &'a [Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>] { - &ecx.machine.stack - } - - #[inline(always)] - fn stack_mut<'a>( - ecx: &'a mut InterpCx<'mir, 'tcx, Self>, - ) -> &'a mut Vec<Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>> { - &mut ecx.machine.stack - } -} - /// The mode that `ConstProp` is allowed to run in for a given `Local`. #[derive(Clone, Copy, Debug, PartialEq)] pub enum ConstPropMode { diff --git a/compiler/rustc_mir_transform/src/const_prop_lint.rs b/compiler/rustc_mir_transform/src/const_prop_lint.rs index b8fecaf635a..aa22b8c7c58 100644 --- a/compiler/rustc_mir_transform/src/const_prop_lint.rs +++ b/compiler/rustc_mir_transform/src/const_prop_lint.rs @@ -3,37 +3,26 @@ use std::fmt::Debug; -use either::Left; - -use rustc_const_eval::interpret::Immediate; -use rustc_const_eval::interpret::{ - InterpCx, InterpResult, MemoryKind, OpTy, Scalar, StackPopCleanup, -}; -use rustc_const_eval::ReportErrorExt; +use rustc_const_eval::interpret::{ImmTy, Projectable}; +use rustc_const_eval::interpret::{InterpCx, InterpResult, Scalar}; +use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::DefKind; use rustc_hir::HirId; use rustc_index::bit_set::BitSet; +use rustc_index::{Idx, IndexVec}; use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::*; use rustc_middle::ty::layout::{LayoutError, LayoutOf, LayoutOfHelpers, TyAndLayout}; -use rustc_middle::ty::GenericArgs; -use rustc_middle::ty::{ - self, ConstInt, Instance, ParamEnv, ScalarInt, Ty, TyCtxt, TypeVisitableExt, -}; +use rustc_middle::ty::{self, ConstInt, ParamEnv, ScalarInt, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::Span; -use rustc_target::abi::{HasDataLayout, Size, TargetDataLayout}; +use rustc_target::abi::{Abi, FieldIdx, HasDataLayout, Size, TargetDataLayout, VariantIdx}; use crate::const_prop::CanConstProp; -use crate::const_prop::ConstPropMachine; use crate::const_prop::ConstPropMode; -use crate::errors::AssertLint; +use crate::dataflow_const_prop::DummyMachine; +use crate::errors::{AssertLint, AssertLintKind}; use crate::MirLint; -/// The maximum number of bytes that we'll allocate space for a local or the return value. -/// Needed for #66397, because otherwise we eval into large places and that can cause OOM or just -/// Severely regress performance. -const MAX_ALLOC_LIMIT: u64 = 1024; - pub struct ConstPropLint; impl<'tcx> MirLint<'tcx> for ConstPropLint { @@ -81,11 +70,85 @@ impl<'tcx> MirLint<'tcx> for ConstPropLint { /// Finds optimization opportunities on the MIR. struct ConstPropagator<'mir, 'tcx> { - ecx: InterpCx<'mir, 'tcx, ConstPropMachine<'mir, 'tcx>>, + ecx: InterpCx<'mir, 'tcx, DummyMachine>, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, worklist: Vec<BasicBlock>, visited_blocks: BitSet<BasicBlock>, + locals: IndexVec<Local, Value<'tcx>>, + body: &'mir Body<'tcx>, + written_only_inside_own_block_locals: FxHashSet<Local>, + can_const_prop: IndexVec<Local, ConstPropMode>, +} + +#[derive(Debug, Clone)] +enum Value<'tcx> { + Immediate(ImmTy<'tcx>), + Aggregate { variant: VariantIdx, fields: IndexVec<FieldIdx, Value<'tcx>> }, + Uninit, +} + +impl<'tcx> From<ImmTy<'tcx>> for Value<'tcx> { + fn from(v: ImmTy<'tcx>) -> Self { + Self::Immediate(v) + } +} + +impl<'tcx> Value<'tcx> { + fn project( + &self, + proj: &[PlaceElem<'tcx>], + prop: &ConstPropagator<'_, 'tcx>, + ) -> Option<&Value<'tcx>> { + let mut this = self; + for proj in proj { + this = match (*proj, this) { + (PlaceElem::Field(idx, _), Value::Aggregate { fields, .. }) => { + fields.get(idx).unwrap_or(&Value::Uninit) + } + (PlaceElem::Index(idx), Value::Aggregate { fields, .. }) => { + let idx = prop.get_const(idx.into())?.immediate()?; + let idx = prop.ecx.read_target_usize(idx).ok()?; + fields.get(FieldIdx::from_u32(idx.try_into().ok()?)).unwrap_or(&Value::Uninit) + } + ( + PlaceElem::ConstantIndex { offset, min_length: _, from_end: false }, + Value::Aggregate { fields, .. }, + ) => fields + .get(FieldIdx::from_u32(offset.try_into().ok()?)) + .unwrap_or(&Value::Uninit), + _ => return None, + }; + } + Some(this) + } + + fn project_mut(&mut self, proj: &[PlaceElem<'_>]) -> Option<&mut Value<'tcx>> { + let mut this = self; + for proj in proj { + this = match (proj, this) { + (PlaceElem::Field(idx, _), Value::Aggregate { fields, .. }) => { + fields.ensure_contains_elem(*idx, || Value::Uninit) + } + (PlaceElem::Field(..), val @ Value::Uninit) => { + *val = Value::Aggregate { + variant: VariantIdx::new(0), + fields: Default::default(), + }; + val.project_mut(&[*proj])? + } + _ => return None, + }; + } + Some(this) + } + + fn immediate(&self) -> Option<&ImmTy<'tcx>> { + match self { + Value::Immediate(op) => Some(op), + _ => None, + } + } } impl<'tcx> LayoutOfHelpers<'tcx> for ConstPropagator<'_, 'tcx> { @@ -121,49 +184,10 @@ impl<'tcx> ty::layout::HasParamEnv<'tcx> for ConstPropagator<'_, 'tcx> { impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { fn new(body: &'mir Body<'tcx>, tcx: TyCtxt<'tcx>) -> ConstPropagator<'mir, 'tcx> { let def_id = body.source.def_id(); - let args = &GenericArgs::identity_for_item(tcx, def_id); let param_env = tcx.param_env_reveal_all_normalized(def_id); let can_const_prop = CanConstProp::check(tcx, param_env, body); - let mut ecx = InterpCx::new( - tcx, - tcx.def_span(def_id), - param_env, - ConstPropMachine::new(can_const_prop), - ); - - let ret_layout = ecx - .layout_of(body.bound_return_ty().instantiate(tcx, args)) - .ok() - // Don't bother allocating memory for large values. - // I don't know how return types can seem to be unsized but this happens in the - // `type/type-unsatisfiable.rs` test. - .filter(|ret_layout| { - ret_layout.is_sized() && ret_layout.size < Size::from_bytes(MAX_ALLOC_LIMIT) - }) - .unwrap_or_else(|| ecx.layout_of(tcx.types.unit).unwrap()); - - let ret = ecx - .allocate(ret_layout, MemoryKind::Stack) - .expect("couldn't perform small allocation") - .into(); - - ecx.push_stack_frame( - Instance::new(def_id, args), - body, - &ret, - StackPopCleanup::Root { cleanup: false }, - ) - .expect("failed to push initial stack frame"); - - for local in body.local_decls.indices() { - // Mark everything initially live. - // This is somewhat dicey since some of them might be unsized and it is incoherent to - // mark those as live... We rely on `local_to_place`/`local_to_op` in the interpreter - // stopping us before those unsized immediates can cause issues deeper in the - // interpreter. - ecx.frame_mut().locals[local].make_live_uninit(); - } + let ecx = InterpCx::new(tcx, tcx.def_span(def_id), param_env, DummyMachine); ConstPropagator { ecx, @@ -171,61 +195,47 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { param_env, worklist: vec![START_BLOCK], visited_blocks: BitSet::new_empty(body.basic_blocks.len()), + locals: IndexVec::from_elem_n(Value::Uninit, body.local_decls.len()), + body, + can_const_prop, + written_only_inside_own_block_locals: Default::default(), } } - fn body(&self) -> &'mir Body<'tcx> { - self.ecx.frame().body - } - fn local_decls(&self) -> &'mir LocalDecls<'tcx> { - &self.body().local_decls + &self.body.local_decls } - fn get_const(&self, place: Place<'tcx>) -> Option<OpTy<'tcx>> { - let op = match self.ecx.eval_place_to_op(place, None) { - Ok(op) => { - if op - .as_mplace_or_imm() - .right() - .is_some_and(|imm| matches!(*imm, Immediate::Uninit)) - { - // Make sure nobody accidentally uses this value. - return None; - } - op - } - Err(e) => { - trace!("get_const failed: {:?}", e.into_kind().debug()); - return None; - } - }; - - // Try to read the local as an immediate so that if it is representable as a scalar, we can - // handle it as such, but otherwise, just return the value as is. - Some(match self.ecx.read_immediate_raw(&op) { - Ok(Left(imm)) => imm.into(), - _ => op, - }) + fn get_const(&self, place: Place<'tcx>) -> Option<&Value<'tcx>> { + self.locals[place.local].project(&place.projection, self) } /// Remove `local` from the pool of `Locals`. Allows writing to them, /// but not reading from them anymore. - fn remove_const(ecx: &mut InterpCx<'mir, 'tcx, ConstPropMachine<'mir, 'tcx>>, local: Local) { - ecx.frame_mut().locals[local].make_live_uninit(); - ecx.machine.written_only_inside_own_block_locals.remove(&local); + fn remove_const(&mut self, local: Local) { + self.locals[local] = Value::Uninit; + self.written_only_inside_own_block_locals.remove(&local); + } + + fn access_mut(&mut self, place: &Place<'_>) -> Option<&mut Value<'tcx>> { + match self.can_const_prop[place.local] { + ConstPropMode::NoPropagation => return None, + ConstPropMode::OnlyInsideOwnBlock => { + self.written_only_inside_own_block_locals.insert(place.local); + } + ConstPropMode::FullConstProp => {} + } + self.locals[place.local].project_mut(place.projection) } fn lint_root(&self, source_info: SourceInfo) -> Option<HirId> { - source_info.scope.lint_root(&self.body().source_scopes) + source_info.scope.lint_root(&self.body.source_scopes) } - fn use_ecx<F, T>(&mut self, location: Location, f: F) -> Option<T> + fn use_ecx<F, T>(&mut self, f: F) -> Option<T> where F: FnOnce(&mut Self) -> InterpResult<'tcx, T>, { - // Overwrite the PC -- whatever the interpreter does to it does not make any sense anyway. - self.ecx.frame_mut().loc = Left(location); match f(self) { Ok(val) => Some(val), Err(error) => { @@ -244,7 +254,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { } /// Returns the value, if any, of evaluating `c`. - fn eval_constant(&mut self, c: &ConstOperand<'tcx>, location: Location) -> Option<OpTy<'tcx>> { + fn eval_constant(&mut self, c: &ConstOperand<'tcx>) -> Option<ImmTy<'tcx>> { // FIXME we need to revisit this for #67176 if c.has_param() { return None; @@ -258,46 +268,62 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { // manually normalized. let val = self.tcx.try_normalize_erasing_regions(self.param_env, c.const_).ok()?; - self.use_ecx(location, |this| this.ecx.eval_mir_constant(&val, Some(c.span), None)) + self.use_ecx(|this| this.ecx.eval_mir_constant(&val, Some(c.span), None))? + .as_mplace_or_imm() + .right() } /// Returns the value, if any, of evaluating `place`. - fn eval_place(&mut self, place: Place<'tcx>, location: Location) -> Option<OpTy<'tcx>> { - trace!("eval_place(place={:?})", place); - self.use_ecx(location, |this| this.ecx.eval_place_to_op(place, None)) + #[instrument(level = "trace", skip(self), ret)] + fn eval_place(&mut self, place: Place<'tcx>) -> Option<ImmTy<'tcx>> { + match self.get_const(place)? { + Value::Immediate(imm) => Some(imm.clone()), + Value::Aggregate { .. } => None, + Value::Uninit => None, + } } /// Returns the value, if any, of evaluating `op`. Calls upon `eval_constant` /// or `eval_place`, depending on the variant of `Operand` used. - fn eval_operand(&mut self, op: &Operand<'tcx>, location: Location) -> Option<OpTy<'tcx>> { + fn eval_operand(&mut self, op: &Operand<'tcx>) -> Option<ImmTy<'tcx>> { match *op { - Operand::Constant(ref c) => self.eval_constant(c, location), - Operand::Move(place) | Operand::Copy(place) => self.eval_place(place, location), + Operand::Constant(ref c) => self.eval_constant(c), + Operand::Move(place) | Operand::Copy(place) => self.eval_place(place), } } - fn report_assert_as_lint(&self, source_info: &SourceInfo, lint: AssertLint<impl Debug>) { + fn report_assert_as_lint( + &self, + location: Location, + lint_kind: AssertLintKind, + assert_kind: AssertKind<impl Debug>, + ) { + let source_info = self.body.source_info(location); if let Some(lint_root) = self.lint_root(*source_info) { - self.tcx.emit_node_span_lint(lint.lint(), lint_root, source_info.span, lint); + let span = source_info.span; + self.tcx.emit_node_span_lint( + lint_kind.lint(), + lint_root, + span, + AssertLint { span, assert_kind, lint_kind }, + ); } } fn check_unary_op(&mut self, op: UnOp, arg: &Operand<'tcx>, location: Location) -> Option<()> { - if let (val, true) = self.use_ecx(location, |this| { - let val = this.ecx.read_immediate(&this.ecx.eval_operand(arg, None)?)?; + let arg = self.eval_operand(arg)?; + if let (val, true) = self.use_ecx(|this| { + let val = this.ecx.read_immediate(&arg)?; let (_res, overflow) = this.ecx.overflowing_unary_op(op, &val)?; Ok((val, overflow)) })? { // `AssertKind` only has an `OverflowNeg` variant, so make sure that is // appropriate to use. assert_eq!(op, UnOp::Neg, "Neg is the only UnOp that can overflow"); - let source_info = self.body().source_info(location); self.report_assert_as_lint( - source_info, - AssertLint::ArithmeticOverflow( - source_info.span, - AssertKind::OverflowNeg(val.to_const_int()), - ), + location, + AssertLintKind::ArithmeticOverflow, + AssertKind::OverflowNeg(val.to_const_int()), ); return None; } @@ -312,11 +338,10 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { right: &Operand<'tcx>, location: Location, ) -> Option<()> { - let r = self.use_ecx(location, |this| { - this.ecx.read_immediate(&this.ecx.eval_operand(right, None)?) - }); - let l = self - .use_ecx(location, |this| this.ecx.read_immediate(&this.ecx.eval_operand(left, None)?)); + let r = + self.eval_operand(right).and_then(|r| self.use_ecx(|this| this.ecx.read_immediate(&r))); + let l = + self.eval_operand(left).and_then(|l| self.use_ecx(|this| this.ecx.read_immediate(&l))); // Check for exceeding shifts *even if* we cannot evaluate the LHS. if matches!(op, BinOp::Shr | BinOp::Shl) { let r = r.clone()?; @@ -328,7 +353,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { let r_bits = r.to_scalar().to_bits(right_size).ok(); if r_bits.is_some_and(|b| b >= left_size.bits() as u128) { debug!("check_binary_op: reporting assert for {:?}", location); - let source_info = self.body().source_info(location); let panic = AssertKind::Overflow( op, match l { @@ -342,27 +366,21 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { }, r.to_const_int(), ); - self.report_assert_as_lint( - source_info, - AssertLint::ArithmeticOverflow(source_info.span, panic), - ); + self.report_assert_as_lint(location, AssertLintKind::ArithmeticOverflow, panic); return None; } } if let (Some(l), Some(r)) = (l, r) { // The remaining operators are handled through `overflowing_binary_op`. - if self.use_ecx(location, |this| { + if self.use_ecx(|this| { let (_res, overflow) = this.ecx.overflowing_binary_op(op, &l, &r)?; Ok(overflow) })? { - let source_info = self.body().source_info(location); self.report_assert_as_lint( - source_info, - AssertLint::ArithmeticOverflow( - source_info.span, - AssertKind::Overflow(op, l.to_const_int(), r.to_const_int()), - ), + location, + AssertLintKind::ArithmeticOverflow, + AssertKind::Overflow(op, l.to_const_int(), r.to_const_int()), ); return None; } @@ -411,7 +429,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { // value the local has right now. // Thus, all locals that have their reference taken // must not take part in propagation. - Self::remove_const(&mut self.ecx, place.local); + self.remove_const(place.local); return None; } @@ -453,17 +471,17 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { cond: &Operand<'tcx>, location: Location, ) -> Option<!> { - let value = &self.eval_operand(cond, location)?; + let value = &self.eval_operand(cond)?; trace!("assertion on {:?} should be {:?}", value, expected); let expected = Scalar::from_bool(expected); - let value_const = self.use_ecx(location, |this| this.ecx.read_scalar(value))?; + let value_const = self.use_ecx(|this| this.ecx.read_scalar(value))?; if expected != value_const { // Poison all places this operand references so that further code // doesn't use the invalid value if let Some(place) = cond.place() { - Self::remove_const(&mut self.ecx, place.local); + self.remove_const(place.local); } enum DbgVal<T> { @@ -481,7 +499,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { let mut eval_to_int = |op| { // This can be `None` if the lhs wasn't const propagated and we just // triggered the assert on the value of the rhs. - self.eval_operand(op, location) + self.eval_operand(op) .and_then(|op| self.ecx.read_immediate(&op).ok()) .map_or(DbgVal::Underscore, |op| DbgVal::Val(op.to_const_int())) }; @@ -503,11 +521,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { // Need proper const propagator for these. _ => return None, }; - let source_info = self.body().source_info(location); - self.report_assert_as_lint( - source_info, - AssertLint::UnconditionalPanic(source_info.span, msg), - ); + self.report_assert_as_lint(location, AssertLintKind::UnconditionalPanic, msg); } None @@ -515,16 +529,176 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { fn ensure_not_propagated(&self, local: Local) { if cfg!(debug_assertions) { + let val = self.get_const(local.into()); assert!( - self.get_const(local.into()).is_none() + matches!(val, Some(Value::Uninit)) || self .layout_of(self.local_decls()[local].ty) .map_or(true, |layout| layout.is_zst()), - "failed to remove values for `{local:?}`, value={:?}", - self.get_const(local.into()), + "failed to remove values for `{local:?}`, value={val:?}", ) } } + + #[instrument(level = "trace", skip(self), ret)] + fn eval_rvalue( + &mut self, + rvalue: &Rvalue<'tcx>, + location: Location, + dest: &Place<'tcx>, + ) -> Option<()> { + if !dest.projection.is_empty() { + return None; + } + use rustc_middle::mir::Rvalue::*; + let layout = self.ecx.layout_of(dest.ty(self.body, self.tcx).ty).ok()?; + trace!(?layout); + + let val: Value<'_> = match *rvalue { + ThreadLocalRef(_) => return None, + + Use(ref operand) => self.eval_operand(operand)?.into(), + + CopyForDeref(place) => self.eval_place(place)?.into(), + + BinaryOp(bin_op, box (ref left, ref right)) => { + let left = self.eval_operand(left)?; + let left = self.use_ecx(|this| this.ecx.read_immediate(&left))?; + + let right = self.eval_operand(right)?; + let right = self.use_ecx(|this| this.ecx.read_immediate(&right))?; + + let val = + self.use_ecx(|this| this.ecx.wrapping_binary_op(bin_op, &left, &right))?; + val.into() + } + + CheckedBinaryOp(bin_op, box (ref left, ref right)) => { + let left = self.eval_operand(left)?; + let left = self.use_ecx(|this| this.ecx.read_immediate(&left))?; + + let right = self.eval_operand(right)?; + let right = self.use_ecx(|this| this.ecx.read_immediate(&right))?; + + let (val, overflowed) = + self.use_ecx(|this| this.ecx.overflowing_binary_op(bin_op, &left, &right))?; + let overflowed = ImmTy::from_bool(overflowed, self.tcx); + Value::Aggregate { + variant: VariantIdx::new(0), + fields: [Value::from(val), overflowed.into()].into_iter().collect(), + } + } + + UnaryOp(un_op, ref operand) => { + let operand = self.eval_operand(operand)?; + let val = self.use_ecx(|this| this.ecx.read_immediate(&operand))?; + + let val = self.use_ecx(|this| this.ecx.wrapping_unary_op(un_op, &val))?; + val.into() + } + + Aggregate(ref kind, ref fields) => Value::Aggregate { + fields: fields + .iter() + .map(|field| self.eval_operand(field).map_or(Value::Uninit, Value::Immediate)) + .collect(), + variant: match **kind { + AggregateKind::Adt(_, variant, _, _, _) => variant, + AggregateKind::Array(_) + | AggregateKind::Tuple + | AggregateKind::Closure(_, _) + | AggregateKind::Coroutine(_, _) => VariantIdx::new(0), + }, + }, + + Repeat(ref op, n) => { + trace!(?op, ?n); + return None; + } + + Len(place) => { + let len = match self.get_const(place)? { + Value::Immediate(src) => src.len(&self.ecx).ok()?, + Value::Aggregate { fields, .. } => fields.len() as u64, + Value::Uninit => match place.ty(self.local_decls(), self.tcx).ty.kind() { + ty::Array(_, n) => n.try_eval_target_usize(self.tcx, self.param_env)?, + _ => return None, + }, + }; + ImmTy::from_scalar(Scalar::from_target_usize(len, self), layout).into() + } + + Ref(..) | AddressOf(..) => return None, + + NullaryOp(ref null_op, ty) => { + let op_layout = self.use_ecx(|this| this.ecx.layout_of(ty))?; + let val = match null_op { + NullOp::SizeOf => op_layout.size.bytes(), + NullOp::AlignOf => op_layout.align.abi.bytes(), + NullOp::OffsetOf(fields) => { + op_layout.offset_of_subfield(self, fields.iter()).bytes() + } + }; + ImmTy::from_scalar(Scalar::from_target_usize(val, self), layout).into() + } + + ShallowInitBox(..) => return None, + + Cast(ref kind, ref value, to) => match kind { + CastKind::IntToInt | CastKind::IntToFloat => { + let value = self.eval_operand(value)?; + let value = self.ecx.read_immediate(&value).ok()?; + let to = self.ecx.layout_of(to).ok()?; + let res = self.ecx.int_to_int_or_float(&value, to).ok()?; + res.into() + } + CastKind::FloatToFloat | CastKind::FloatToInt => { + let value = self.eval_operand(value)?; + let value = self.ecx.read_immediate(&value).ok()?; + let to = self.ecx.layout_of(to).ok()?; + let res = self.ecx.float_to_float_or_int(&value, to).ok()?; + res.into() + } + CastKind::Transmute => { + let value = self.eval_operand(value)?; + let to = self.ecx.layout_of(to).ok()?; + // `offset` for immediates only supports scalar/scalar-pair ABIs, + // so bail out if the target is not one. + match (value.layout.abi, to.abi) { + (Abi::Scalar(..), Abi::Scalar(..)) => {} + (Abi::ScalarPair(..), Abi::ScalarPair(..)) => {} + _ => return None, + } + + value.offset(Size::ZERO, to, &self.ecx).ok()?.into() + } + _ => return None, + }, + + Discriminant(place) => { + let variant = match self.get_const(place)? { + Value::Immediate(op) => { + let op = op.clone(); + self.use_ecx(|this| this.ecx.read_discriminant(&op))? + } + Value::Aggregate { variant, .. } => *variant, + Value::Uninit => return None, + }; + let imm = self.use_ecx(|this| { + this.ecx.discriminant_for_variant( + place.ty(this.local_decls(), this.tcx).ty, + variant, + ) + })?; + imm.into() + } + }; + trace!(?val); + + *self.access_mut(dest)? = val; + + Some(()) + } } impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> { @@ -546,7 +720,7 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> { fn visit_constant(&mut self, constant: &ConstOperand<'tcx>, location: Location) { trace!("visit_constant: {:?}", constant); self.super_constant(constant, location); - self.eval_constant(constant, location); + self.eval_constant(constant); } fn visit_assign(&mut self, place: &Place<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) { @@ -554,15 +728,12 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> { let Some(()) = self.check_rvalue(rvalue, location) else { return }; - match self.ecx.machine.can_const_prop[place.local] { + match self.can_const_prop[place.local] { // Do nothing if the place is indirect. _ if place.is_indirect() => {} ConstPropMode::NoPropagation => self.ensure_not_propagated(place.local), ConstPropMode::OnlyInsideOwnBlock | ConstPropMode::FullConstProp => { - if self - .use_ecx(location, |this| this.ecx.eval_rvalue_into_place(rvalue, *place)) - .is_none() - { + if self.eval_rvalue(rvalue, location, place).is_none() { // Const prop failed, so erase the destination, ensuring that whatever happens // from here on, does not know about the previous value. // This is important in case we have @@ -578,7 +749,7 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> { Nuking the entire site from orbit, it's the only way to be sure", place, ); - Self::remove_const(&mut self.ecx, place.local); + self.remove_const(place.local); } } } @@ -592,28 +763,24 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> { self.super_statement(statement, location); match statement.kind { - StatementKind::SetDiscriminant { ref place, .. } => { - match self.ecx.machine.can_const_prop[place.local] { + StatementKind::SetDiscriminant { ref place, variant_index } => { + match self.can_const_prop[place.local] { // Do nothing if the place is indirect. _ if place.is_indirect() => {} ConstPropMode::NoPropagation => self.ensure_not_propagated(place.local), ConstPropMode::FullConstProp | ConstPropMode::OnlyInsideOwnBlock => { - if self.use_ecx(location, |this| this.ecx.statement(statement)).is_some() { - trace!("propped discriminant into {:?}", place); - } else { - Self::remove_const(&mut self.ecx, place.local); + match self.access_mut(place) { + Some(Value::Aggregate { variant, .. }) => *variant = variant_index, + _ => self.remove_const(place.local), } } } } StatementKind::StorageLive(local) => { - let frame = self.ecx.frame_mut(); - frame.locals[local].make_live_uninit(); + self.remove_const(local); } StatementKind::StorageDead(local) => { - let frame = self.ecx.frame_mut(); - // We don't actually track liveness, so the local remains live. But forget its value. - frame.locals[local].make_live_uninit(); + self.remove_const(local); } _ => {} } @@ -626,9 +793,8 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> { self.check_assertion(*expected, msg, cond, location); } TerminatorKind::SwitchInt { ref discr, ref targets } => { - if let Some(ref value) = self.eval_operand(discr, location) - && let Some(value_const) = - self.use_ecx(location, |this| this.ecx.read_scalar(value)) + if let Some(ref value) = self.eval_operand(discr) + && let Some(value_const) = self.use_ecx(|this| this.ecx.read_scalar(value)) && let Ok(constant) = value_const.try_to_int() && let Ok(constant) = constant.to_bits(constant.size()) { @@ -665,7 +831,7 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> { // which were modified in the current block. // Take it out of the ecx so we can get a mutable reference to the ecx for `remove_const`. let mut written_only_inside_own_block_locals = - std::mem::take(&mut self.ecx.machine.written_only_inside_own_block_locals); + std::mem::take(&mut self.written_only_inside_own_block_locals); // This loop can get very hot for some bodies: it check each local in each bb. // To avoid this quadratic behaviour, we only clear the locals that were modified inside @@ -673,17 +839,13 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> { // The order in which we remove consts does not matter. #[allow(rustc::potential_query_instability)] for local in written_only_inside_own_block_locals.drain() { - debug_assert_eq!( - self.ecx.machine.can_const_prop[local], - ConstPropMode::OnlyInsideOwnBlock - ); - Self::remove_const(&mut self.ecx, local); + debug_assert_eq!(self.can_const_prop[local], ConstPropMode::OnlyInsideOwnBlock); + self.remove_const(local); } - self.ecx.machine.written_only_inside_own_block_locals = - written_only_inside_own_block_locals; + self.written_only_inside_own_block_locals = written_only_inside_own_block_locals; if cfg!(debug_assertions) { - for (local, &mode) in self.ecx.machine.can_const_prop.iter_enumerated() { + for (local, &mode) in self.can_const_prop.iter_enumerated() { match mode { ConstPropMode::FullConstProp => {} ConstPropMode::NoPropagation | ConstPropMode::OnlyInsideOwnBlock => { diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index a11d224e8f1..ad5fcc3acf1 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -59,167 +59,154 @@ impl<'tcx> MirPass<'tcx> for InstrumentCoverage { _ => {} } - trace!("InstrumentCoverage starting for {def_id:?}"); - Instrumentor::new(tcx, mir_body).inject_counters(); - trace!("InstrumentCoverage done for {def_id:?}"); + instrument_function_for_coverage(tcx, mir_body); } } -struct Instrumentor<'a, 'tcx> { - tcx: TyCtxt<'tcx>, - mir_body: &'a mut mir::Body<'tcx>, - hir_info: ExtractedHirInfo, - basic_coverage_blocks: CoverageGraph, -} - -impl<'a, 'tcx> Instrumentor<'a, 'tcx> { - fn new(tcx: TyCtxt<'tcx>, mir_body: &'a mut mir::Body<'tcx>) -> Self { - let hir_info = extract_hir_info(tcx, mir_body.source.def_id().expect_local()); +fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir::Body<'tcx>) { + let def_id = mir_body.source.def_id(); + let _span = debug_span!("instrument_function_for_coverage", ?def_id).entered(); - debug!(?hir_info, "instrumenting {:?}", mir_body.source.def_id()); + let hir_info = extract_hir_info(tcx, def_id.expect_local()); + let basic_coverage_blocks = CoverageGraph::from_mir(mir_body); - let basic_coverage_blocks = CoverageGraph::from_mir(mir_body); + //////////////////////////////////////////////////// + // Compute coverage spans from the `CoverageGraph`. + let Some(coverage_spans) = + spans::generate_coverage_spans(mir_body, &hir_info, &basic_coverage_blocks) + else { + // No relevant spans were found in MIR, so skip instrumenting this function. + return; + }; - Self { tcx, mir_body, hir_info, basic_coverage_blocks } + //////////////////////////////////////////////////// + // Create an optimized mix of `Counter`s and `Expression`s for the `CoverageGraph`. Ensure + // every coverage span has a `Counter` or `Expression` assigned to its `BasicCoverageBlock` + // and all `Expression` dependencies (operands) are also generated, for any other + // `BasicCoverageBlock`s not already associated with a coverage span. + let bcb_has_coverage_spans = |bcb| coverage_spans.bcb_has_coverage_spans(bcb); + let coverage_counters = + CoverageCounters::make_bcb_counters(&basic_coverage_blocks, bcb_has_coverage_spans); + + let mappings = create_mappings(tcx, &hir_info, &coverage_spans, &coverage_counters); + if mappings.is_empty() { + // No spans could be converted into valid mappings, so skip this function. + debug!("no spans could be converted into valid mappings; skipping"); + return; } - fn inject_counters(&'a mut self) { - //////////////////////////////////////////////////// - // Compute coverage spans from the `CoverageGraph`. - let Some(coverage_spans) = CoverageSpans::generate_coverage_spans( - self.mir_body, - &self.hir_info, - &self.basic_coverage_blocks, - ) else { - // No relevant spans were found in MIR, so skip instrumenting this function. - return; - }; - - //////////////////////////////////////////////////// - // Create an optimized mix of `Counter`s and `Expression`s for the `CoverageGraph`. Ensure - // every coverage span has a `Counter` or `Expression` assigned to its `BasicCoverageBlock` - // and all `Expression` dependencies (operands) are also generated, for any other - // `BasicCoverageBlock`s not already associated with a coverage span. - let bcb_has_coverage_spans = |bcb| coverage_spans.bcb_has_coverage_spans(bcb); - let coverage_counters = CoverageCounters::make_bcb_counters( - &self.basic_coverage_blocks, - bcb_has_coverage_spans, - ); + inject_coverage_statements( + mir_body, + &basic_coverage_blocks, + bcb_has_coverage_spans, + &coverage_counters, + ); - let mappings = self.create_mappings(&coverage_spans, &coverage_counters); - if mappings.is_empty() { - // No spans could be converted into valid mappings, so skip this function. - debug!("no spans could be converted into valid mappings; skipping"); - return; - } + mir_body.function_coverage_info = Some(Box::new(FunctionCoverageInfo { + function_source_hash: hir_info.function_source_hash, + num_counters: coverage_counters.num_counters(), + expressions: coverage_counters.into_expressions(), + mappings, + })); +} - self.inject_coverage_statements(bcb_has_coverage_spans, &coverage_counters); +/// For each coverage span extracted from MIR, create a corresponding +/// mapping. +/// +/// Precondition: All BCBs corresponding to those spans have been given +/// coverage counters. +fn create_mappings<'tcx>( + tcx: TyCtxt<'tcx>, + hir_info: &ExtractedHirInfo, + coverage_spans: &CoverageSpans, + coverage_counters: &CoverageCounters, +) -> Vec<Mapping> { + let source_map = tcx.sess.source_map(); + let body_span = hir_info.body_span; + + let source_file = source_map.lookup_source_file(body_span.lo()); + use rustc_session::RemapFileNameExt; + let file_name = Symbol::intern(&source_file.name.for_codegen(tcx.sess).to_string_lossy()); + + let term_for_bcb = |bcb| { + coverage_counters + .bcb_counter(bcb) + .expect("all BCBs with spans were given counters") + .as_term() + }; - self.mir_body.function_coverage_info = Some(Box::new(FunctionCoverageInfo { - function_source_hash: self.hir_info.function_source_hash, - num_counters: coverage_counters.num_counters(), - expressions: coverage_counters.into_expressions(), - mappings, - })); - } + coverage_spans + .all_bcb_mappings() + .filter_map(|&BcbMapping { kind: bcb_mapping_kind, span }| { + let kind = match bcb_mapping_kind { + BcbMappingKind::Code(bcb) => MappingKind::Code(term_for_bcb(bcb)), + }; + let code_region = make_code_region(source_map, file_name, span, body_span)?; + Some(Mapping { kind, code_region }) + }) + .collect::<Vec<_>>() +} - /// For each coverage span extracted from MIR, create a corresponding - /// mapping. - /// - /// Precondition: All BCBs corresponding to those spans have been given - /// coverage counters. - fn create_mappings( - &self, - coverage_spans: &CoverageSpans, - coverage_counters: &CoverageCounters, - ) -> Vec<Mapping> { - let source_map = self.tcx.sess.source_map(); - let body_span = self.hir_info.body_span; - - let source_file = source_map.lookup_source_file(body_span.lo()); - use rustc_session::RemapFileNameExt; - let file_name = - Symbol::intern(&source_file.name.for_codegen(self.tcx.sess).to_string_lossy()); - - let term_for_bcb = |bcb| { - coverage_counters - .bcb_counter(bcb) - .expect("all BCBs with spans were given counters") - .as_term() +/// For each BCB node or BCB edge that has an associated coverage counter, +/// inject any necessary coverage statements into MIR. +fn inject_coverage_statements<'tcx>( + mir_body: &mut mir::Body<'tcx>, + basic_coverage_blocks: &CoverageGraph, + bcb_has_coverage_spans: impl Fn(BasicCoverageBlock) -> bool, + coverage_counters: &CoverageCounters, +) { + // Process the counters associated with BCB nodes. + for (bcb, counter_kind) in coverage_counters.bcb_node_counters() { + let do_inject = match counter_kind { + // Counter-increment statements always need to be injected. + BcbCounter::Counter { .. } => true, + // The only purpose of expression-used statements is to detect + // when a mapping is unreachable, so we only inject them for + // expressions with one or more mappings. + BcbCounter::Expression { .. } => bcb_has_coverage_spans(bcb), }; - - coverage_spans - .all_bcb_mappings() - .filter_map(|&BcbMapping { kind: bcb_mapping_kind, span }| { - let kind = match bcb_mapping_kind { - BcbMappingKind::Code(bcb) => MappingKind::Code(term_for_bcb(bcb)), - }; - let code_region = make_code_region(source_map, file_name, span, body_span)?; - Some(Mapping { kind, code_region }) - }) - .collect::<Vec<_>>() + if do_inject { + inject_statement( + mir_body, + make_mir_coverage_kind(counter_kind), + basic_coverage_blocks[bcb].leader_bb(), + ); + } } - /// For each BCB node or BCB edge that has an associated coverage counter, - /// inject any necessary coverage statements into MIR. - fn inject_coverage_statements( - &mut self, - bcb_has_coverage_spans: impl Fn(BasicCoverageBlock) -> bool, - coverage_counters: &CoverageCounters, - ) { - // Process the counters associated with BCB nodes. - for (bcb, counter_kind) in coverage_counters.bcb_node_counters() { - let do_inject = match counter_kind { - // Counter-increment statements always need to be injected. - BcbCounter::Counter { .. } => true, - // The only purpose of expression-used statements is to detect - // when a mapping is unreachable, so we only inject them for - // expressions with one or more mappings. - BcbCounter::Expression { .. } => bcb_has_coverage_spans(bcb), - }; - if do_inject { - inject_statement( - self.mir_body, - self.make_mir_coverage_kind(counter_kind), - self.basic_coverage_blocks[bcb].leader_bb(), - ); - } + // Process the counters associated with BCB edges. + for (from_bcb, to_bcb, counter_kind) in coverage_counters.bcb_edge_counters() { + let do_inject = match counter_kind { + // Counter-increment statements always need to be injected. + BcbCounter::Counter { .. } => true, + // BCB-edge expressions never have mappings, so they never need + // a corresponding statement. + BcbCounter::Expression { .. } => false, + }; + if !do_inject { + continue; } - // Process the counters associated with BCB edges. - for (from_bcb, to_bcb, counter_kind) in coverage_counters.bcb_edge_counters() { - let do_inject = match counter_kind { - // Counter-increment statements always need to be injected. - BcbCounter::Counter { .. } => true, - // BCB-edge expressions never have mappings, so they never need - // a corresponding statement. - BcbCounter::Expression { .. } => false, - }; - if !do_inject { - continue; - } - - // We need to inject a coverage statement into a new BB between the - // last BB of `from_bcb` and the first BB of `to_bcb`. - let from_bb = self.basic_coverage_blocks[from_bcb].last_bb(); - let to_bb = self.basic_coverage_blocks[to_bcb].leader_bb(); + // We need to inject a coverage statement into a new BB between the + // last BB of `from_bcb` and the first BB of `to_bcb`. + let from_bb = basic_coverage_blocks[from_bcb].last_bb(); + let to_bb = basic_coverage_blocks[to_bcb].leader_bb(); - let new_bb = inject_edge_counter_basic_block(self.mir_body, from_bb, to_bb); - debug!( - "Edge {from_bcb:?} (last {from_bb:?}) -> {to_bcb:?} (leader {to_bb:?}) \ + let new_bb = inject_edge_counter_basic_block(mir_body, from_bb, to_bb); + debug!( + "Edge {from_bcb:?} (last {from_bb:?}) -> {to_bcb:?} (leader {to_bb:?}) \ requires a new MIR BasicBlock {new_bb:?} for edge counter {counter_kind:?}", - ); + ); - // Inject a counter into the newly-created BB. - inject_statement(self.mir_body, self.make_mir_coverage_kind(counter_kind), new_bb); - } + // Inject a counter into the newly-created BB. + inject_statement(mir_body, make_mir_coverage_kind(counter_kind), new_bb); } +} - fn make_mir_coverage_kind(&self, counter_kind: &BcbCounter) -> CoverageKind { - match *counter_kind { - BcbCounter::Counter { id } => CoverageKind::CounterIncrement { id }, - BcbCounter::Expression { id } => CoverageKind::ExpressionUsed { id }, - } +fn make_mir_coverage_kind(counter_kind: &BcbCounter) -> CoverageKind { + match *counter_kind { + BcbCounter::Counter { id } => CoverageKind::CounterIncrement { id }, + BcbCounter::Expression { id } => CoverageKind::ExpressionUsed { id }, } } @@ -329,7 +316,7 @@ fn make_code_region( start_line = source_map.doctest_offset_line(&file.name, start_line); end_line = source_map.doctest_offset_line(&file.name, end_line); - Some(CodeRegion { + check_code_region(CodeRegion { file_name, start_line: start_line as u32, start_col: start_col as u32, @@ -338,6 +325,39 @@ fn make_code_region( }) } +/// If `llvm-cov` sees a code region that is improperly ordered (end < start), +/// it will immediately exit with a fatal error. To prevent that from happening, +/// discard regions that are improperly ordered, or might be interpreted in a +/// way that makes them improperly ordered. +fn check_code_region(code_region: CodeRegion) -> Option<CodeRegion> { + let CodeRegion { file_name: _, start_line, start_col, end_line, end_col } = code_region; + + // Line/column coordinates are supposed to be 1-based. If we ever emit + // coordinates of 0, `llvm-cov` might misinterpret them. + let all_nonzero = [start_line, start_col, end_line, end_col].into_iter().all(|x| x != 0); + // Coverage mappings use the high bit of `end_col` to indicate that a + // region is actually a "gap" region, so make sure it's unset. + let end_col_has_high_bit_unset = (end_col & (1 << 31)) == 0; + // If a region is improperly ordered (end < start), `llvm-cov` will exit + // with a fatal error, which is inconvenient for users and hard to debug. + let is_ordered = (start_line, start_col) <= (end_line, end_col); + + if all_nonzero && end_col_has_high_bit_unset && is_ordered { + Some(code_region) + } else { + debug!( + ?code_region, + ?all_nonzero, + ?end_col_has_high_bit_unset, + ?is_ordered, + "Skipping code region that would be misinterpreted or rejected by LLVM" + ); + // If this happens in a debug build, ICE to make it easier to notice. + debug_assert!(false, "Improper code region: {code_region:?}"); + None + } +} + fn is_eligible_for_coverage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { // Only instrument functions, methods, and closures (not constants since they are evaluated // at compile time by Miri). @@ -351,7 +371,18 @@ fn is_eligible_for_coverage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { return false; } + // Don't instrument functions with `#[automatically_derived]` on their + // enclosing impl block, on the assumption that most users won't care about + // coverage for derived impls. + if let Some(impl_of) = tcx.impl_of_method(def_id.to_def_id()) + && tcx.is_automatically_derived(impl_of) + { + trace!("InstrumentCoverage skipped for {def_id:?} (automatically derived)"); + return false; + } + if tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::NO_COVERAGE) { + trace!("InstrumentCoverage skipped for {def_id:?} (`#[coverage(off)]`)"); return false; } diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index 81f6c831206..dee6a3b7143 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -26,45 +26,6 @@ pub(super) struct CoverageSpans { } impl CoverageSpans { - /// Extracts coverage-relevant spans from MIR, and associates them with - /// their corresponding BCBs. - /// - /// Returns `None` if no coverage-relevant spans could be extracted. - pub(super) fn generate_coverage_spans( - mir_body: &mir::Body<'_>, - hir_info: &ExtractedHirInfo, - basic_coverage_blocks: &CoverageGraph, - ) -> Option<Self> { - let mut mappings = vec![]; - - let coverage_spans = CoverageSpansGenerator::generate_coverage_spans( - mir_body, - hir_info, - basic_coverage_blocks, - ); - mappings.extend(coverage_spans.into_iter().map(|CoverageSpan { bcb, span, .. }| { - // Each span produced by the generator represents an ordinary code region. - BcbMapping { kind: BcbMappingKind::Code(bcb), span } - })); - - if mappings.is_empty() { - return None; - } - - // Identify which BCBs have one or more mappings. - let mut bcb_has_mappings = BitSet::new_empty(basic_coverage_blocks.num_nodes()); - let mut insert = |bcb| { - bcb_has_mappings.insert(bcb); - }; - for &BcbMapping { kind, span: _ } in &mappings { - match kind { - BcbMappingKind::Code(bcb) => insert(bcb), - } - } - - Some(Self { bcb_has_mappings, mappings }) - } - pub(super) fn bcb_has_coverage_spans(&self, bcb: BasicCoverageBlock) -> bool { self.bcb_has_mappings.contains(bcb) } @@ -74,6 +35,43 @@ impl CoverageSpans { } } +/// Extracts coverage-relevant spans from MIR, and associates them with +/// their corresponding BCBs. +/// +/// Returns `None` if no coverage-relevant spans could be extracted. +pub(super) fn generate_coverage_spans( + mir_body: &mir::Body<'_>, + hir_info: &ExtractedHirInfo, + basic_coverage_blocks: &CoverageGraph, +) -> Option<CoverageSpans> { + let mut mappings = vec![]; + + let sorted_spans = + from_mir::mir_to_initial_sorted_coverage_spans(mir_body, hir_info, basic_coverage_blocks); + let coverage_spans = SpansRefiner::refine_sorted_spans(basic_coverage_blocks, sorted_spans); + mappings.extend(coverage_spans.into_iter().map(|CoverageSpan { bcb, span, .. }| { + // Each span produced by the generator represents an ordinary code region. + BcbMapping { kind: BcbMappingKind::Code(bcb), span } + })); + + if mappings.is_empty() { + return None; + } + + // Identify which BCBs have one or more mappings. + let mut bcb_has_mappings = BitSet::new_empty(basic_coverage_blocks.num_nodes()); + let mut insert = |bcb| { + bcb_has_mappings.insert(bcb); + }; + for &BcbMapping { kind, span: _ } in &mappings { + match kind { + BcbMappingKind::Code(bcb) => insert(bcb), + } + } + + Some(CoverageSpans { bcb_has_mappings, mappings }) +} + /// A BCB is deconstructed into one or more `Span`s. Each `Span` maps to a `CoverageSpan` that /// references the originating BCB and one or more MIR `Statement`s and/or `Terminator`s. /// Initially, the `Span`s come from the `Statement`s and `Terminator`s, but subsequent @@ -130,7 +128,7 @@ impl CoverageSpan { /// * Merge spans that represent continuous (both in source code and control flow), non-branching /// execution /// * Carve out (leave uncovered) any span that will be counted by another MIR (notably, closures) -struct CoverageSpansGenerator<'a> { +struct SpansRefiner<'a> { /// The BasicCoverageBlock Control Flow Graph (BCB CFG). basic_coverage_blocks: &'a CoverageGraph, @@ -173,40 +171,15 @@ struct CoverageSpansGenerator<'a> { refined_spans: Vec<CoverageSpan>, } -impl<'a> CoverageSpansGenerator<'a> { - /// Generate a minimal set of `CoverageSpan`s, each representing a contiguous code region to be - /// counted. - /// - /// The basic steps are: - /// - /// 1. Extract an initial set of spans from the `Statement`s and `Terminator`s of each - /// `BasicCoverageBlockData`. - /// 2. Sort the spans by span.lo() (starting position). Spans that start at the same position - /// are sorted with longer spans before shorter spans; and equal spans are sorted - /// (deterministically) based on "dominator" relationship (if any). - /// 3. Traverse the spans in sorted order to identify spans that can be dropped (for instance, - /// if another span or spans are already counting the same code region), or should be merged - /// into a broader combined span (because it represents a contiguous, non-branching, and - /// uninterrupted region of source code). - /// - /// Closures are exposed in their enclosing functions as `Assign` `Rvalue`s, and since - /// closures have their own MIR, their `Span` in their enclosing function should be left - /// "uncovered". - /// - /// Note the resulting vector of `CoverageSpan`s may not be fully sorted (and does not need - /// to be). - pub(super) fn generate_coverage_spans( - mir_body: &mir::Body<'_>, - hir_info: &ExtractedHirInfo, +impl<'a> SpansRefiner<'a> { + /// Takes the initial list of (sorted) spans extracted from MIR, and "refines" + /// them by merging compatible adjacent spans, removing redundant spans, + /// and carving holes in spans when they overlap in unwanted ways. + fn refine_sorted_spans( basic_coverage_blocks: &'a CoverageGraph, + sorted_spans: Vec<CoverageSpan>, ) -> Vec<CoverageSpan> { - let sorted_spans = from_mir::mir_to_initial_sorted_coverage_spans( - mir_body, - hir_info, - basic_coverage_blocks, - ); - - let coverage_spans = Self { + let this = Self { basic_coverage_blocks, sorted_spans_iter: sorted_spans.into_iter(), some_curr: None, @@ -217,7 +190,7 @@ impl<'a> CoverageSpansGenerator<'a> { refined_spans: Vec::with_capacity(basic_coverage_blocks.num_nodes() * 2), }; - coverage_spans.to_refined_spans() + this.to_refined_spans() } /// Iterate through the sorted `CoverageSpan`s, and return the refined list of merged and diff --git a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs index 1b6dfccd574..8d8e8e61327 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs @@ -12,6 +12,12 @@ use crate::coverage::graph::{ use crate::coverage::spans::CoverageSpan; use crate::coverage::ExtractedHirInfo; +/// Traverses the MIR body to produce an initial collection of coverage-relevant +/// spans, each associated with a node in the coverage graph (BCB) and possibly +/// other metadata. +/// +/// The returned spans are sorted in a specific order that is expected by the +/// subsequent span-refinement step. pub(super) fn mir_to_initial_sorted_coverage_spans( mir_body: &mir::Body<'_>, hir_info: &ExtractedHirInfo, diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index ad12bce9b02..d5f22b2cdbc 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -403,7 +403,12 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { operand, &mut |elem, op| match elem { TrackElem::Field(idx) => self.ecx.project_field(op, idx.as_usize()).ok(), - TrackElem::Variant(idx) => self.ecx.project_downcast(op, idx).ok(), + TrackElem::Variant(idx) => { + if op.layout.for_variant(&self.ecx, idx).abi.is_uninhabited() { + return None; + } + self.ecx.project_downcast(op, idx).ok() + } TrackElem::Discriminant => { let variant = self.ecx.read_discriminant(op).ok()?; let discr_value = diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs index 2ee660ddc9b..4574cb4d28d 100644 --- a/compiler/rustc_mir_transform/src/errors.rs +++ b/compiler/rustc_mir_transform/src/errors.rs @@ -201,45 +201,39 @@ impl<'a> DecorateLint<'a, ()> for UnsafeOpInUnsafeFn { } } -pub(crate) enum AssertLint<P> { - ArithmeticOverflow(Span, AssertKind<P>), - UnconditionalPanic(Span, AssertKind<P>), +pub(crate) struct AssertLint<P> { + pub span: Span, + pub assert_kind: AssertKind<P>, + pub lint_kind: AssertLintKind, +} + +pub(crate) enum AssertLintKind { + ArithmeticOverflow, + UnconditionalPanic, } impl<'a, P: std::fmt::Debug> DecorateLint<'a, ()> for AssertLint<P> { fn decorate_lint<'b>(self, diag: &'b mut DiagnosticBuilder<'a, ()>) { - let span = self.span(); - let assert_kind = self.panic(); - let message = assert_kind.diagnostic_message(); - assert_kind.add_args(&mut |name, value| { + let message = self.assert_kind.diagnostic_message(); + self.assert_kind.add_args(&mut |name, value| { diag.arg(name, value); }); - diag.span_label(span, message); + diag.span_label(self.span, message); } fn msg(&self) -> DiagnosticMessage { - match self { - AssertLint::ArithmeticOverflow(..) => fluent::mir_transform_arithmetic_overflow, - AssertLint::UnconditionalPanic(..) => fluent::mir_transform_operation_will_panic, + match self.lint_kind { + AssertLintKind::ArithmeticOverflow => fluent::mir_transform_arithmetic_overflow, + AssertLintKind::UnconditionalPanic => fluent::mir_transform_operation_will_panic, } } } -impl<P> AssertLint<P> { +impl AssertLintKind { pub fn lint(&self) -> &'static Lint { match self { - AssertLint::ArithmeticOverflow(..) => lint::builtin::ARITHMETIC_OVERFLOW, - AssertLint::UnconditionalPanic(..) => lint::builtin::UNCONDITIONAL_PANIC, - } - } - pub fn span(&self) -> Span { - match self { - AssertLint::ArithmeticOverflow(sp, _) | AssertLint::UnconditionalPanic(sp, _) => *sp, - } - } - pub fn panic(self) -> AssertKind<P> { - match self { - AssertLint::ArithmeticOverflow(_, p) | AssertLint::UnconditionalPanic(_, p) => p, + AssertLintKind::ArithmeticOverflow => lint::builtin::ARITHMETIC_OVERFLOW, + AssertLintKind::UnconditionalPanic => lint::builtin::UNCONDITIONAL_PANIC, } } } diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 8b991268819..b86244e5a4b 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -1113,7 +1113,13 @@ fn find_vtable_types_for_unsizing<'tcx>( assert_eq!(source_adt_def, target_adt_def); let CustomCoerceUnsized::Struct(coerce_index) = - crate::custom_coerce_unsize_info(tcx, source_ty, target_ty); + match crate::custom_coerce_unsize_info(tcx, source_ty, target_ty) { + Ok(ccu) => ccu, + Err(e) => { + let e = Ty::new_error(tcx.tcx, e); + return (e, e); + } + }; let source_fields = &source_adt_def.non_enum_variant().fields; let target_fields = &target_adt_def.non_enum_variant().fields; diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs index 2f5f2d15cd4..3fa40db3c80 100644 --- a/compiler/rustc_monomorphize/src/lib.rs +++ b/compiler/rustc_monomorphize/src/lib.rs @@ -15,6 +15,7 @@ use rustc_middle::query::{Providers, TyCtxtAt}; use rustc_middle::traits; use rustc_middle::ty::adjustment::CustomCoerceUnsized; use rustc_middle::ty::{self, Ty}; +use rustc_span::ErrorGuaranteed; mod collector; mod errors; @@ -28,7 +29,7 @@ fn custom_coerce_unsize_info<'tcx>( tcx: TyCtxtAt<'tcx>, source_ty: Ty<'tcx>, target_ty: Ty<'tcx>, -) -> CustomCoerceUnsized { +) -> Result<CustomCoerceUnsized, ErrorGuaranteed> { let trait_ref = ty::TraitRef::from_lang_item( tcx.tcx, LangItem::CoerceUnsized, @@ -40,7 +41,7 @@ fn custom_coerce_unsize_info<'tcx>( Ok(traits::ImplSource::UserDefined(traits::ImplSourceUserDefinedData { impl_def_id, .. - })) => tcx.coerce_unsized_info(impl_def_id).custom_kind.unwrap(), + })) => Ok(tcx.coerce_unsized_info(impl_def_id)?.custom_kind.unwrap()), impl_source => { bug!("invalid `CoerceUnsized` impl_source: {:?}", impl_source); } diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 0d4ee1e8dca..ae10695fae4 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -24,13 +24,13 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId, CRATE_DEF_ID}; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{AssocItemKind, ForeignItemKind, ItemId, PatKind}; -use rustc_middle::bug; use rustc_middle::hir::nested_filter; use rustc_middle::middle::privacy::{EffectiveVisibilities, EffectiveVisibility, Level}; use rustc_middle::query::Providers; use rustc_middle::ty::GenericArgs; use rustc_middle::ty::{self, Const, GenericParamDefKind}; use rustc_middle::ty::{TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; +use rustc_middle::{bug, span_bug}; use rustc_session::lint; use rustc_span::hygiene::Transparency; use rustc_span::symbol::{kw, sym, Ident}; @@ -1064,29 +1064,22 @@ impl<'tcx> Visitor<'tcx> for NamePrivacyVisitor<'tcx> { struct TypePrivacyVisitor<'tcx> { tcx: TyCtxt<'tcx>, + module_def_id: LocalModDefId, maybe_typeck_results: Option<&'tcx ty::TypeckResults<'tcx>>, - current_item: LocalDefId, span: Span, } impl<'tcx> TypePrivacyVisitor<'tcx> { - /// Gets the type-checking results for the current body. - /// As this will ICE if called outside bodies, only call when working with - /// `Expr` or `Pat` nodes (they are guaranteed to be found only in bodies). - #[track_caller] - fn typeck_results(&self) -> &'tcx ty::TypeckResults<'tcx> { - self.maybe_typeck_results - .expect("`TypePrivacyVisitor::typeck_results` called outside of body") - } - fn item_is_accessible(&self, did: DefId) -> bool { - self.tcx.visibility(did).is_accessible_from(self.current_item, self.tcx) + self.tcx.visibility(did).is_accessible_from(self.module_def_id, self.tcx) } // Take node-id of an expression or pattern and check its type for privacy. fn check_expr_pat_type(&mut self, id: hir::HirId, span: Span) -> bool { self.span = span; - let typeck_results = self.typeck_results(); + let typeck_results = self + .maybe_typeck_results + .unwrap_or_else(|| span_bug!(span, "`hir::Expr` or `hir::Pat` outside of a body")); let result: ControlFlow<()> = try { self.visit(typeck_results.node_type(id))?; self.visit(typeck_results.node_args(id))?; @@ -1107,35 +1100,13 @@ impl<'tcx> TypePrivacyVisitor<'tcx> { } impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> { - type NestedFilter = nested_filter::All; - - /// We want to visit items in the context of their containing - /// module and so forth, so supply a crate for doing a deep walk. - fn nested_visit_map(&mut self) -> Self::Map { - self.tcx.hir() - } - - fn visit_mod(&mut self, _m: &'tcx hir::Mod<'tcx>, _s: Span, _n: hir::HirId) { - // Don't visit nested modules, since we run a separate visitor walk - // for each module in `effective_visibilities` - } - - fn visit_nested_body(&mut self, body: hir::BodyId) { + fn visit_nested_body(&mut self, body_id: hir::BodyId) { let old_maybe_typeck_results = - self.maybe_typeck_results.replace(self.tcx.typeck_body(body)); - let body = self.tcx.hir().body(body); - self.visit_body(body); + self.maybe_typeck_results.replace(self.tcx.typeck_body(body_id)); + self.visit_body(self.tcx.hir().body(body_id)); self.maybe_typeck_results = old_maybe_typeck_results; } - fn visit_generic_arg(&mut self, generic_arg: &'tcx hir::GenericArg<'tcx>) { - match generic_arg { - hir::GenericArg::Type(t) => self.visit_ty(t), - hir::GenericArg::Infer(inf) => self.visit_infer(inf), - hir::GenericArg::Lifetime(_) | hir::GenericArg::Const(_) => {} - } - } - fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx>) { self.span = hir_ty.span; if let Some(typeck_results) = self.maybe_typeck_results { @@ -1163,19 +1134,19 @@ impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> { return; } } else { - // We don't do anything for const infers here. + // FIXME: check types of const infers here. } } else { - bug!("visit_infer without typeck_results"); + span_bug!(self.span, "`hir::InferArg` outside of a body"); } intravisit::walk_inf(self, inf); } fn visit_trait_ref(&mut self, trait_ref: &'tcx hir::TraitRef<'tcx>) { self.span = trait_ref.path.span; - if self.maybe_typeck_results.is_none() { - // Avoid calling `hir_trait_to_predicates` in bodies, it will ICE. - // The traits' privacy in bodies is already checked as a part of trait object types. + if self.maybe_typeck_results.is_some() { + // Privacy of traits in bodies is checked as a part of trait object types. + } else { let bounds = rustc_hir_analysis::hir_trait_to_predicates( self.tcx, trait_ref, @@ -1223,7 +1194,10 @@ impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> { hir::ExprKind::MethodCall(segment, ..) => { // Method calls have to be checked specially. self.span = segment.ident.span; - if let Some(def_id) = self.typeck_results().type_dependent_def_id(expr.hir_id) { + let typeck_results = self + .maybe_typeck_results + .unwrap_or_else(|| span_bug!(self.span, "`hir::Expr` outside of a body")); + if let Some(def_id) = typeck_results.type_dependent_def_id(expr.hir_id) { if self.visit(self.tcx.type_of(def_id).instantiate_identity()).is_break() { return; } @@ -1251,9 +1225,13 @@ impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> { Res::Def(kind, def_id) => Some((kind, def_id)), _ => None, }, - hir::QPath::TypeRelative(..) | hir::QPath::LangItem(..) => self - .maybe_typeck_results - .and_then(|typeck_results| typeck_results.type_dependent_def(id)), + hir::QPath::TypeRelative(..) | hir::QPath::LangItem(..) => { + match self.maybe_typeck_results { + Some(typeck_results) => typeck_results.type_dependent_def(id), + // FIXME: Check type-relative associated types in signatures. + None => None, + } + } }; let def = def.filter(|(kind, _)| { matches!( @@ -1307,15 +1285,6 @@ impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> { intravisit::walk_local(self, local); } - - // Check types in item interfaces. - fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { - let orig_current_item = mem::replace(&mut self.current_item, item.owner_id.def_id); - let old_maybe_typeck_results = self.maybe_typeck_results.take(); - intravisit::walk_item(self, item); - self.maybe_typeck_results = old_maybe_typeck_results; - self.current_item = orig_current_item; - } } impl<'tcx> DefIdVisitor<'tcx> for TypePrivacyVisitor<'tcx> { @@ -1785,13 +1754,8 @@ fn check_mod_privacy(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) { // Check privacy of explicitly written types and traits as well as // inferred types of expressions and patterns. - let mut visitor = TypePrivacyVisitor { - tcx, - maybe_typeck_results: None, - current_item: module_def_id.to_local_def_id(), - span, - }; - intravisit::walk_mod(&mut visitor, module, hir_id); + let mut visitor = TypePrivacyVisitor { tcx, module_def_id, maybe_typeck_results: None, span }; + tcx.hir().visit_item_likes_in_module(module_def_id, &mut visitor); } fn effective_visibilities(tcx: TyCtxt<'_>, (): ()) -> &EffectiveVisibilities { diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 24aa336c68f..891a83ce80c 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -332,20 +332,6 @@ impl Session { } } - // FIXME(matthewjasper) Remove this method, it should never be needed. - pub fn track_errors<F, T>(&self, f: F) -> Result<T, ErrorGuaranteed> - where - F: FnOnce() -> T, - { - let old_count = self.dcx().err_count(); - let result = f(); - if self.dcx().err_count() == old_count { - Ok(result) - } else { - Err(self.dcx().delayed_bug("`self.err_count()` changed but an error was not emitted")) - } - } - /// Used for code paths of expensive computations that should only take place when /// warnings or errors are emitted. If no messages are emitted ("good path"), then /// it's likely a bug. @@ -1524,16 +1510,25 @@ pub trait RemapFileNameExt { where Self: 'a; - fn for_scope(&self, sess: &Session, scopes: RemapPathScopeComponents) -> Self::Output<'_>; + /// Returns a possibly remapped filename based on the passed scope and remap cli options. + /// + /// One and only one scope should be passed to this method. For anything related to + /// "codegen" see the [`RemapFileNameExt::for_codegen`] method. + fn for_scope(&self, sess: &Session, scope: RemapPathScopeComponents) -> Self::Output<'_>; + /// Return a possibly remapped filename, to be used in "codegen" related parts. fn for_codegen(&self, sess: &Session) -> Self::Output<'_>; } impl RemapFileNameExt for rustc_span::FileName { type Output<'a> = rustc_span::FileNameDisplay<'a>; - fn for_scope(&self, sess: &Session, scopes: RemapPathScopeComponents) -> Self::Output<'_> { - if sess.opts.unstable_opts.remap_path_scope.contains(scopes) { + fn for_scope(&self, sess: &Session, scope: RemapPathScopeComponents) -> Self::Output<'_> { + assert!( + scope.bits().count_ones() == 1, + "one and only one scope should be passed to for_scope" + ); + if sess.opts.unstable_opts.remap_path_scope.contains(scope) { self.prefer_remapped_unconditionaly() } else { self.prefer_local() @@ -1552,8 +1547,12 @@ impl RemapFileNameExt for rustc_span::FileName { impl RemapFileNameExt for rustc_span::RealFileName { type Output<'a> = &'a Path; - fn for_scope(&self, sess: &Session, scopes: RemapPathScopeComponents) -> Self::Output<'_> { - if sess.opts.unstable_opts.remap_path_scope.contains(scopes) { + fn for_scope(&self, sess: &Session, scope: RemapPathScopeComponents) -> Self::Output<'_> { + assert!( + scope.bits().count_ones() == 1, + "one and only one scope should be passed to for_scope" + ); + if sess.opts.unstable_opts.remap_path_scope.contains(scope) { self.remapped_path_if_available() } else { self.local_path_if_available() diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 90a38b26f73..efd5e3727b9 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -425,8 +425,14 @@ symbols! { assume, assume_init, async_await, + async_call, + async_call_mut, + async_call_once, async_closure, + async_fn, async_fn_in_trait, + async_fn_mut, + async_fn_once, async_fn_track_caller, async_for_loop, async_iterator, @@ -910,6 +916,7 @@ symbols! { io_stderr, io_stdout, irrefutable_let_patterns, + is_val_statically_known, isa_attribute, isize, issue, @@ -1793,6 +1800,7 @@ symbols! { warn, wasm_abi, wasm_import_module, + wasm_preview2, wasm_target_feature, while_let, windows, diff --git a/compiler/rustc_target/src/spec/base/wasm.rs b/compiler/rustc_target/src/spec/base/wasm.rs index 87ade9e58cf..4b4d2aca26e 100644 --- a/compiler/rustc_target/src/spec/base/wasm.rs +++ b/compiler/rustc_target/src/spec/base/wasm.rs @@ -38,9 +38,6 @@ pub fn options() -> TargetOptions { // supposed to be imported and have all other symbols generate errors if // they remain undefined. concat!($prefix, "--allow-undefined"), - // Rust code should never have warnings, and warnings are often - // indicative of bugs, let's prevent them. - concat!($prefix, "--fatal-warnings"), // LLD only implements C++-like demangling, which doesn't match our own // mangling scheme. Tell LLD to not demangle anything and leave it up to // us to demangle these symbols later. Currently rustc does not perform diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 884bd23e8cc..ead3be7fd52 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1574,6 +1574,7 @@ supported_targets! { ("wasm32-unknown-emscripten", wasm32_unknown_emscripten), ("wasm32-unknown-unknown", wasm32_unknown_unknown), ("wasm32-wasi", wasm32_wasi), + ("wasm32-wasi-preview2", wasm32_wasi_preview2), ("wasm32-wasi-preview1-threads", wasm32_wasi_preview1_threads), ("wasm64-unknown-unknown", wasm64_unknown_unknown), diff --git a/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs b/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs index 394f02ecac4..e9e874297d3 100644 --- a/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs +++ b/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs @@ -5,10 +5,7 @@ use crate::spec::{ pub fn target() -> Target { // Reset flags for non-Em flavors back to empty to satisfy sanity checking tests. let pre_link_args = LinkArgs::new(); - let post_link_args = TargetOptions::link_args( - LinkerFlavor::EmCc, - &["-sABORTING_MALLOC=0", "-Wl,--fatal-warnings"], - ); + let post_link_args = TargetOptions::link_args(LinkerFlavor::EmCc, &["-sABORTING_MALLOC=0"]); let opts = TargetOptions { os: "emscripten".into(), diff --git a/compiler/rustc_target/src/spec/targets/wasm32_wasi_preview1_threads.rs b/compiler/rustc_target/src/spec/targets/wasm32_wasi_preview1_threads.rs index 28ea4cc9ece..389c67f8ae9 100644 --- a/compiler/rustc_target/src/spec/targets/wasm32_wasi_preview1_threads.rs +++ b/compiler/rustc_target/src/spec/targets/wasm32_wasi_preview1_threads.rs @@ -72,11 +72,12 @@ //! best we can with this target. Don't start relying on too much here unless //! you know what you're getting in to! -use crate::spec::{base, crt_objects, Cc, LinkSelfContainedDefault, LinkerFlavor, Target}; +use crate::spec::{base, crt_objects, cvs, Cc, LinkSelfContainedDefault, LinkerFlavor, Target}; pub fn target() -> Target { let mut options = base::wasm::options(); + options.families = cvs!["wasm", "wasi"]; options.os = "wasi".into(); options.add_pre_link_args( diff --git a/compiler/rustc_target/src/spec/targets/wasm32_wasi_preview2.rs b/compiler/rustc_target/src/spec/targets/wasm32_wasi_preview2.rs new file mode 100644 index 00000000000..fc44e5d4cbc --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/wasm32_wasi_preview2.rs @@ -0,0 +1,64 @@ +//! The `wasm32-wasi-preview2` target is the next evolution of the +//! wasm32-wasi target. While the wasi specification is still under +//! active development, the {review 2 iteration is considered an "island +//! of stability" that should allow users to rely on it indefinitely. +//! +//! The `wasi` target is a proposal to define a standardized set of WebAssembly +//! component imports that allow it to interoperate with the host system in a +//! standardized way. This set of imports is intended to empower WebAssembly +//! binaries with host capabilities such as filesystem access, network access, etc. +//! +//! Wasi Preview 2 relies on the WebAssembly component model which is an extension of +//! the core WebAssembly specification which allows interoperability between WebAssembly +//! modules (known as "components") through high-level, shared-nothing APIs instead of the +//! low-level, shared-everything linear memory model of the core WebAssembly specification. +//! +//! You can see more about wasi at <https://wasi.dev> and the component model at +//! <https://github.com/WebAssembly/component-model>. + +use crate::spec::crt_objects; +use crate::spec::LinkSelfContainedDefault; +use crate::spec::{base, Target}; + +pub fn target() -> Target { + let mut options = base::wasm::options(); + + options.os = "wasi".into(); + options.env = "preview2".into(); + options.linker = Some("wasm-component-ld".into()); + + options.pre_link_objects_self_contained = crt_objects::pre_wasi_self_contained(); + options.post_link_objects_self_contained = crt_objects::post_wasi_self_contained(); + + // FIXME: Figure out cases in which WASM needs to link with a native toolchain. + options.link_self_contained = LinkSelfContainedDefault::True; + + // Right now this is a bit of a workaround but we're currently saying that + // the target by default has a static crt which we're taking as a signal + // for "use the bundled crt". If that's turned off then the system's crt + // will be used, but this means that default usage of this target doesn't + // need an external compiler but it's still interoperable with an external + // compiler if configured correctly. + options.crt_static_default = true; + options.crt_static_respected = true; + + // Allow `+crt-static` to create a "cdylib" output which is just a wasm file + // without a main function. + options.crt_static_allows_dylibs = true; + + // WASI's `sys::args::init` function ignores its arguments; instead, + // `args::args()` makes the WASI API calls itself. + options.main_needs_argc_argv = false; + + // And, WASI mangles the name of "main" to distinguish between different + // signatures. + options.entry_name = "__main_void".into(); + + Target { + llvm_target: "wasm32-unknown-unknown".into(), + pointer_width: 32, + data_layout: "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20".into(), + arch: "wasm32".into(), + options, + } +} diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index e31aaaa1969..6a40ffebdad 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -7,7 +7,7 @@ use super::{ use crate::errors; use crate::infer::InferCtxt; -use crate::traits::{NormalizeExt, ObligationCtxt}; +use crate::traits::{ImplDerivedObligationCause, NormalizeExt, ObligationCtxt}; use hir::def::CtorOf; use rustc_data_structures::fx::FxHashSet; @@ -2973,7 +2973,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { | ObligationCauseCode::ObjectTypeBound(..) => {} ObligationCauseCode::RustCall => { if let Some(pred) = predicate.to_opt_poly_trait_pred() - && Some(pred.def_id()) == self.tcx.lang_items().sized_trait() + && Some(pred.def_id()) == tcx.lang_items().sized_trait() { err.note("argument required to be sized due to `extern \"rust-call\"` ABI"); } @@ -3022,15 +3022,15 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let def_id = trait_pred.def_id(); let visible_item = if let Some(local) = def_id.as_local() { // Check for local traits being reachable. - let vis = &self.tcx.resolutions(()).effective_visibilities; + let vis = &tcx.resolutions(()).effective_visibilities; // Account for non-`pub` traits in the root of the local crate. - let is_locally_reachable = self.tcx.parent(def_id).is_crate_root(); + let is_locally_reachable = tcx.parent(def_id).is_crate_root(); vis.is_reachable(local) || is_locally_reachable } else { // Check for foreign traits being reachable. - self.tcx.visible_parent_map(()).get(&def_id).is_some() + tcx.visible_parent_map(()).get(&def_id).is_some() }; - if Some(def_id) == self.tcx.lang_items().sized_trait() + if Some(def_id) == tcx.lang_items().sized_trait() && let Some(hir::Node::TraitItem(hir::TraitItem { ident, kind: hir::TraitItemKind::Type(bounds, None), @@ -3039,7 +3039,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // Do not suggest relaxing if there is an explicit `Sized` obligation. && !bounds.iter() .filter_map(|bound| bound.trait_ref()) - .any(|tr| tr.trait_def_id() == self.tcx.lang_items().sized_trait()) + .any(|tr| tr.trait_def_id() == tcx.lang_items().sized_trait()) { let (span, separator) = if let [.., last] = bounds { (last.span().shrink_to_hi(), " +") @@ -3102,10 +3102,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } ObligationCauseCode::Coercion { source, target } => { let mut file = None; - let source = - self.tcx.short_ty_string(self.resolve_vars_if_possible(source), &mut file); - let target = - self.tcx.short_ty_string(self.resolve_vars_if_possible(target), &mut file); + let source = tcx.short_ty_string(self.resolve_vars_if_possible(source), &mut file); + let target = tcx.short_ty_string(self.resolve_vars_if_possible(target), &mut file); err.note(with_forced_trimmed_paths!(format!( "required for the cast from `{source}` to `{target}`", ))); @@ -3155,10 +3153,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } else { // FIXME: we may suggest array::repeat instead err.help("consider using `core::array::from_fn` to initialize the array"); - err.help("see https://doc.rust-lang.org/stable/std/array/fn.from_fn.html# for more information"); + err.help("see https://doc.rust-lang.org/stable/std/array/fn.from_fn.html for more information"); } - if self.tcx.sess.is_nightly_build() + if tcx.sess.is_nightly_build() && matches!(is_constable, IsConstable::Fn | IsConstable::Ctor) { err.help( @@ -3168,8 +3166,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } } ObligationCauseCode::VariableType(hir_id) => { - let parent_node = self.tcx.hir().parent_id(hir_id); - match self.tcx.opt_hir_node(parent_node) { + let parent_node = tcx.hir().parent_id(hir_id); + match tcx.opt_hir_node(parent_node) { Some(Node::Local(hir::Local { ty: Some(ty), .. })) => { err.span_suggestion_verbose( ty.span.shrink_to_lo(), @@ -3207,7 +3205,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err.note("all local variables must have a statically known size"); } } - if !self.tcx.features().unsized_locals { + if !tcx.features().unsized_locals { err.help("unsized locals are gated as an unstable feature"); } } @@ -3289,7 +3287,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err.note("all function arguments must have a statically known size"); } if tcx.sess.opts.unstable_features.is_nightly_build() - && !self.tcx.features().unsized_fn_params + && !tcx.features().unsized_fn_params { err.help("unsized fn params are gated as an unstable feature"); } @@ -3358,7 +3356,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { "all values captured by value by a closure must have a statically known size", ); let hir::ExprKind::Closure(closure) = - self.tcx.hir_node_by_def_id(closure_def_id).expect_expr().kind + tcx.hir_node_by_def_id(closure_def_id).expect_expr().kind else { bug!("expected closure in SizedClosureCapture obligation"); }; @@ -3369,7 +3367,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } } ObligationCauseCode::SizedCoroutineInterior(coroutine_def_id) => { - let what = match self.tcx.coroutine_kind(coroutine_def_id) { + let what = match tcx.coroutine_kind(coroutine_def_id) { None | Some(hir::CoroutineKind::Coroutine(_)) | Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _)) => { @@ -3420,10 +3418,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { 'print: { if !is_upvar_tys_infer_tuple { let mut file = None; - let ty_str = self.tcx.short_ty_string(ty, &mut file); + let ty_str = tcx.short_ty_string(ty, &mut file); let msg = format!("required because it appears within the type `{ty_str}`"); match ty.kind() { - ty::Adt(def, _) => match self.tcx.opt_item_ident(def.did()) { + ty::Adt(def, _) => match tcx.opt_item_ident(def.did()) { Some(ident) => err.span_note(ident.span, msg), None => err.note(msg), }, @@ -3446,7 +3444,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { { break 'print; } - err.span_note(self.tcx.def_span(def_id), msg) + err.span_note(tcx.def_span(def_id), msg) } ty::CoroutineWitness(def_id, args) => { use std::fmt::Write; @@ -3463,7 +3461,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err.note(msg.trim_end_matches(", ").to_string()) } ty::Coroutine(def_id, _) => { - let sp = self.tcx.def_span(def_id); + let sp = tcx.def_span(def_id); // Special-case this to say "async block" instead of `[static coroutine]`. let kind = tcx.coroutine_kind(def_id).unwrap(); @@ -3475,7 +3473,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ) } ty::Closure(def_id, _) => err.span_note( - self.tcx.def_span(def_id), + tcx.def_span(def_id), "required because it's used within this closure", ), ty::Str => err.note("`str` is considered to contain a `[u8]` slice for auto trait purposes"), @@ -3519,14 +3517,12 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { self.resolve_vars_if_possible(data.derived.parent_trait_pred); let parent_def_id = parent_trait_pred.def_id(); let mut file = None; - let self_ty = - self.tcx.short_ty_string(parent_trait_pred.skip_binder().self_ty(), &mut file); - let msg = format!( - "required for `{self_ty}` to implement `{}`", - parent_trait_pred.print_modifiers_and_trait_path() - ); + let self_ty_str = + tcx.short_ty_string(parent_trait_pred.skip_binder().self_ty(), &mut file); + let trait_name = parent_trait_pred.print_modifiers_and_trait_path().to_string(); + let msg = format!("required for `{self_ty_str}` to implement `{trait_name}`"); let mut is_auto_trait = false; - match self.tcx.hir().get_if_local(data.impl_or_alias_def_id) { + match tcx.hir().get_if_local(data.impl_or_alias_def_id) { Some(Node::Item(hir::Item { kind: hir::ItemKind::Trait(is_auto, ..), ident, @@ -3538,7 +3534,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err.span_note(ident.span, msg); } Some(Node::Item(hir::Item { - kind: hir::ItemKind::Impl(hir::Impl { of_trait, self_ty, .. }), + kind: hir::ItemKind::Impl(hir::Impl { of_trait, self_ty, generics, .. }), .. })) => { let mut spans = Vec::with_capacity(2); @@ -3565,6 +3561,15 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ); } err.span_note(spans, msg); + point_at_assoc_type_restriction( + tcx, + err, + &self_ty_str, + &trait_name, + predicate, + &generics, + &data, + ); } _ => { err.note(msg); @@ -3618,9 +3623,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { pluralize!(count) )); let mut file = None; - let self_ty = self - .tcx - .short_ty_string(parent_trait_pred.skip_binder().self_ty(), &mut file); + let self_ty = + tcx.short_ty_string(parent_trait_pred.skip_binder().self_ty(), &mut file); err.note(format!( "required for `{self_ty}` to implement `{}`", parent_trait_pred.print_modifiers_and_trait_path() @@ -3678,10 +3682,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { multispan.push_span_label(span, "required by this bound"); err.span_note( multispan, - format!( - "required by a bound on the type alias `{}`", - self.infcx.tcx.item_name(def_id) - ), + format!("required by a bound on the type alias `{}`", tcx.item_name(def_id)), ); } ObligationCauseCode::FunctionArgumentObligation { @@ -3712,25 +3713,23 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { }); } ObligationCauseCode::CompareImplItemObligation { trait_item_def_id, kind, .. } => { - let item_name = self.tcx.item_name(trait_item_def_id); + let item_name = tcx.item_name(trait_item_def_id); let msg = format!( "the requirement `{predicate}` appears on the `impl`'s {kind} \ `{item_name}` but not on the corresponding trait's {kind}", ); - let sp = self - .tcx + let sp = tcx .opt_item_ident(trait_item_def_id) .map(|i| i.span) - .unwrap_or_else(|| self.tcx.def_span(trait_item_def_id)); + .unwrap_or_else(|| tcx.def_span(trait_item_def_id)); let mut assoc_span: MultiSpan = sp.into(); assoc_span.push_span_label( sp, format!("this trait's {kind} doesn't have the requirement `{predicate}`"), ); - if let Some(ident) = self - .tcx + if let Some(ident) = tcx .opt_associated_item(trait_item_def_id) - .and_then(|i| self.tcx.opt_item_ident(i.container_id(self.tcx))) + .and_then(|i| tcx.opt_item_ident(i.container_id(tcx))) { assoc_span.push_span_label(ident.span, "in this trait"); } @@ -4820,6 +4819,29 @@ fn hint_missing_borrow<'tcx>( } } +/// Collect all the paths that reference `Self`. +/// Used to suggest replacing associated types with an explicit type in `where` clauses. +#[derive(Debug)] +pub struct SelfVisitor<'v> { + pub paths: Vec<&'v hir::Ty<'v>>, + pub name: Option<Symbol>, +} + +impl<'v> Visitor<'v> for SelfVisitor<'v> { + fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { + if let hir::TyKind::Path(path) = ty.kind + && let hir::QPath::TypeRelative(inner_ty, segment) = path + && (Some(segment.ident.name) == self.name || self.name.is_none()) + && let hir::TyKind::Path(inner_path) = inner_ty.kind + && let hir::QPath::Resolved(None, inner_path) = inner_path + && let Res::SelfTyAlias { .. } = inner_path.res + { + self.paths.push(ty); + } + hir::intravisit::walk_ty(self, ty); + } +} + /// Collect all the returned expressions within the input expression. /// Used to point at the return spans when we want to suggest some change to them. #[derive(Default)] @@ -5064,6 +5086,134 @@ pub fn suggest_desugaring_async_fn_to_impl_future_in_trait<'tcx>( Some(sugg) } +/// On `impl` evaluation cycles, look for `Self::AssocTy` restrictions in `where` clauses, explain +/// they are not allowed and if possible suggest alternatives. +fn point_at_assoc_type_restriction( + tcx: TyCtxt<'_>, + err: &mut Diagnostic, + self_ty_str: &str, + trait_name: &str, + predicate: ty::Predicate<'_>, + generics: &hir::Generics<'_>, + data: &ImplDerivedObligationCause<'_>, +) { + let ty::PredicateKind::Clause(clause) = predicate.kind().skip_binder() else { + return; + }; + let ty::ClauseKind::Projection(proj) = clause else { + return; + }; + let name = tcx.item_name(proj.projection_ty.def_id); + let mut predicates = generics.predicates.iter().peekable(); + let mut prev: Option<&hir::WhereBoundPredicate<'_>> = None; + while let Some(pred) = predicates.next() { + let hir::WherePredicate::BoundPredicate(pred) = pred else { + continue; + }; + let mut bounds = pred.bounds.iter().peekable(); + while let Some(bound) = bounds.next() { + let Some(trait_ref) = bound.trait_ref() else { + continue; + }; + if bound.span() != data.span { + continue; + } + if let hir::TyKind::Path(path) = pred.bounded_ty.kind + && let hir::QPath::TypeRelative(ty, segment) = path + && segment.ident.name == name + && let hir::TyKind::Path(inner_path) = ty.kind + && let hir::QPath::Resolved(None, inner_path) = inner_path + && let Res::SelfTyAlias { .. } = inner_path.res + { + // The following block is to determine the right span to delete for this bound + // that will leave valid code after the suggestion is applied. + let span = if pred.origin == hir::PredicateOrigin::WhereClause + && generics + .predicates + .iter() + .filter(|p| { + matches!( + p, + hir::WherePredicate::BoundPredicate(p) + if hir::PredicateOrigin::WhereClause == p.origin + ) + }) + .count() + == 1 + { + // There's only one `where` bound, that needs to be removed. Remove the whole + // `where` clause. + generics.where_clause_span + } else if let Some(hir::WherePredicate::BoundPredicate(next)) = predicates.peek() + && pred.origin == next.origin + { + // There's another bound, include the comma for the current one. + pred.span.until(next.span) + } else if let Some(prev) = prev + && pred.origin == prev.origin + { + // Last bound, try to remove the previous comma. + prev.span.shrink_to_hi().to(pred.span) + } else if pred.origin == hir::PredicateOrigin::WhereClause { + pred.span.with_hi(generics.where_clause_span.hi()) + } else { + pred.span + }; + + err.span_suggestion_verbose( + span, + "associated type for the current `impl` cannot be restricted in `where` \ + clauses, remove this bound", + "", + Applicability::MaybeIncorrect, + ); + } + if let Some(new) = + tcx.associated_items(data.impl_or_alias_def_id).find_by_name_and_kind( + tcx, + Ident::with_dummy_span(name), + ty::AssocKind::Type, + data.impl_or_alias_def_id, + ) + { + // The associated type is specified in the `impl` we're + // looking at. Point at it. + let span = tcx.def_span(new.def_id); + err.span_label( + span, + format!( + "associated type `<{self_ty_str} as {trait_name}>::{name}` is specified \ + here", + ), + ); + // Search for the associated type `Self::{name}`, get + // its type and suggest replacing the bound with it. + let mut visitor = SelfVisitor { paths: vec![], name: Some(name) }; + visitor.visit_trait_ref(trait_ref); + for path in visitor.paths { + err.span_suggestion_verbose( + path.span, + "replace the associated type with the type specified in this `impl`", + tcx.type_of(new.def_id).skip_binder().to_string(), + Applicability::MachineApplicable, + ); + } + } else { + let mut visitor = SelfVisitor { paths: vec![], name: None }; + visitor.visit_trait_ref(trait_ref); + let span: MultiSpan = + visitor.paths.iter().map(|p| p.span).collect::<Vec<Span>>().into(); + err.span_note( + span, + "associated types for the current `impl` cannot be restricted in `where` \ + clauses", + ); + } + } + prev = Some(pred); + } +} + fn get_deref_type_and_refs(mut ty: Ty<'_>) -> (Ty<'_>, Vec<hir::Mutability>) { let mut refs = vec![]; diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 5d917dc6fbb..76a53a9366a 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -2517,6 +2517,66 @@ extern "rust-intrinsic" { where G: FnOnce<ARG, Output = RET>, F: FnOnce<ARG, Output = RET>; + + /// Returns whether the argument's value is statically known at + /// compile-time. + /// + /// This is useful when there is a way of writing the code that will + /// be *faster* when some variables have known values, but *slower* + /// in the general case: an `if is_val_statically_known(var)` can be used + /// to select between these two variants. The `if` will be optimized away + /// and only the desired branch remains. + /// + /// Formally speaking, this function non-deterministically returns `true` + /// or `false`, and the caller has to ensure sound behavior for both cases. + /// In other words, the following code has *Undefined Behavior*: + /// + /// ``` + /// #![feature(is_val_statically_known)] + /// #![feature(core_intrinsics)] + /// # #![allow(internal_features)] + /// use std::hint::unreachable_unchecked; + /// use std::intrinsics::is_val_statically_known; + /// + /// unsafe { + /// if !is_val_statically_known(0) { unreachable_unchecked(); } + /// } + /// ``` + /// + /// This also means that the following code's behavior is unspecified; it + /// may panic, or it may not: + /// + /// ```no_run + /// #![feature(is_val_statically_known)] + /// #![feature(core_intrinsics)] + /// # #![allow(internal_features)] + /// use std::intrinsics::is_val_statically_known; + /// + /// unsafe { + /// assert_eq!(is_val_statically_known(0), is_val_statically_known(0)); + /// } + /// ``` + /// + /// Unsafe code may not rely on `is_val_statically_known` returning any + /// particular value, ever. However, the compiler will generally make it + /// return `true` only if the value of the argument is actually known. + /// + /// When calling this in a `const fn`, both paths must be semantically + /// equivalent, that is, the result of the `true` branch and the `false` + /// branch must return the same value and have the same side-effects *no + /// matter what*. + #[rustc_const_unstable(feature = "is_val_statically_known", issue = "none")] + #[rustc_nounwind] + #[cfg(not(bootstrap))] + pub fn is_val_statically_known<T: Copy>(arg: T) -> bool; +} + +// FIXME: Seems using `unstable` here completely ignores `rustc_allow_const_fn_unstable` +// and thus compiling stage0 core doesn't work. +#[rustc_const_stable(feature = "is_val_statically_known", since = "0.0.0")] +#[cfg(bootstrap)] +pub const unsafe fn is_val_statically_known<T: Copy>(_arg: T) -> bool { + false } // Some functions are defined here because they accidentally got made diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index ead8cbe0e2f..2af242d4b50 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -200,6 +200,7 @@ // // Language features: // tidy-alphabetical-start +#![cfg_attr(not(bootstrap), feature(is_val_statically_known))] #![feature(abi_unadjusted)] #![feature(adt_const_params)] #![feature(allow_internal_unsafe)] diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index d052dcc3e6e..bb35b6128ea 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -1374,26 +1374,59 @@ macro_rules! int_impl { #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] + #[rustc_allow_const_fn_unstable(is_val_statically_known, const_int_unchecked_arith)] #[inline] pub const fn checked_pow(self, mut exp: u32) -> Option<Self> { - if exp == 0 { - return Some(1); - } - let mut base = self; - let mut acc: Self = 1; - - while exp > 1 { - if (exp & 1) == 1 { - acc = try_opt!(acc.checked_mul(base)); + // SAFETY: This path has the same behavior as the other. + if unsafe { intrinsics::is_val_statically_known(self) } + && self.unsigned_abs().is_power_of_two() + { + if self == 1 { // Avoid divide by zero + return Some(1); } - exp /= 2; - base = try_opt!(base.checked_mul(base)); + if self == -1 { // Avoid divide by zero + return Some(if exp & 1 != 0 { -1 } else { 1 }); + } + // SAFETY: We just checked this is a power of two. and above zero. + let power_used = unsafe { intrinsics::cttz_nonzero(self.wrapping_abs()) as u32 }; + if exp > Self::BITS / power_used { return None; } // Division of constants is free + + // SAFETY: exp <= Self::BITS / power_used + let res = unsafe { intrinsics::unchecked_shl( + 1 as Self, + intrinsics::unchecked_mul(power_used, exp) as Self + )}; + // LLVM doesn't always optimize out the checks + // at the ir level. + + let sign = self.is_negative() && exp & 1 != 0; + if !sign && res == Self::MIN { + None + } else if sign { + Some(res.wrapping_neg()) + } else { + Some(res) + } + } else { + if exp == 0 { + return Some(1); + } + let mut base = self; + let mut acc: Self = 1; + + while exp > 1 { + if (exp & 1) == 1 { + acc = try_opt!(acc.checked_mul(base)); + } + exp /= 2; + base = try_opt!(base.checked_mul(base)); + } + // since exp!=0, finally the exp must be 1. + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary and may cause a + // needless overflow. + acc.checked_mul(base) } - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - acc.checked_mul(base) } /// Strict exponentiation. Computes `self.pow(exp)`, panicking if @@ -2058,27 +2091,58 @@ macro_rules! int_impl { #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] + #[rustc_allow_const_fn_unstable(is_val_statically_known, const_int_unchecked_arith)] #[inline] pub const fn wrapping_pow(self, mut exp: u32) -> Self { - if exp == 0 { - return 1; - } - let mut base = self; - let mut acc: Self = 1; - - while exp > 1 { - if (exp & 1) == 1 { - acc = acc.wrapping_mul(base); + // SAFETY: This path has the same behavior as the other. + if unsafe { intrinsics::is_val_statically_known(self) } + && self.unsigned_abs().is_power_of_two() + { + if self == 1 { // Avoid divide by zero + return 1; + } + if self == -1 { // Avoid divide by zero + return if exp & 1 != 0 { -1 } else { 1 }; + } + // SAFETY: We just checked this is a power of two. and above zero. + let power_used = unsafe { intrinsics::cttz_nonzero(self.wrapping_abs()) as u32 }; + if exp > Self::BITS / power_used { return 0; } // Division of constants is free + + // SAFETY: exp <= Self::BITS / power_used + let res = unsafe { intrinsics::unchecked_shl( + 1 as Self, + intrinsics::unchecked_mul(power_used, exp) as Self + )}; + // LLVM doesn't always optimize out the checks + // at the ir level. + + let sign = self.is_negative() && exp & 1 != 0; + if sign { + res.wrapping_neg() + } else { + res + } + } else { + if exp == 0 { + return 1; + } + let mut base = self; + let mut acc: Self = 1; + + while exp > 1 { + if (exp & 1) == 1 { + acc = acc.wrapping_mul(base); + } + exp /= 2; + base = base.wrapping_mul(base); } - exp /= 2; - base = base.wrapping_mul(base); - } - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - acc.wrapping_mul(base) + // since exp!=0, finally the exp must be 1. + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary and may cause a + // needless overflow. + acc.wrapping_mul(base) + } } /// Calculates `self` + `rhs` @@ -2561,36 +2625,68 @@ macro_rules! int_impl { #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] + #[rustc_allow_const_fn_unstable(is_val_statically_known, const_int_unchecked_arith)] #[inline] pub const fn overflowing_pow(self, mut exp: u32) -> (Self, bool) { - if exp == 0 { - return (1,false); - } - let mut base = self; - let mut acc: Self = 1; - let mut overflown = false; - // Scratch space for storing results of overflowing_mul. - let mut r; - - while exp > 1 { - if (exp & 1) == 1 { - r = acc.overflowing_mul(base); - acc = r.0; + // SAFETY: This path has the same behavior as the other. + if unsafe { intrinsics::is_val_statically_known(self) } + && self.unsigned_abs().is_power_of_two() + { + if self == 1 { // Avoid divide by zero + return (1, false); + } + if self == -1 { // Avoid divide by zero + return (if exp & 1 != 0 { -1 } else { 1 }, false); + } + // SAFETY: We just checked this is a power of two. and above zero. + let power_used = unsafe { intrinsics::cttz_nonzero(self.wrapping_abs()) as u32 }; + if exp > Self::BITS / power_used { return (0, true); } // Division of constants is free + + // SAFETY: exp <= Self::BITS / power_used + let res = unsafe { intrinsics::unchecked_shl( + 1 as Self, + intrinsics::unchecked_mul(power_used, exp) as Self + )}; + // LLVM doesn't always optimize out the checks + // at the ir level. + + let sign = self.is_negative() && exp & 1 != 0; + let overflow = res == Self::MIN; + if sign { + (res.wrapping_neg(), overflow) + } else { + (res, overflow) + } + } else { + if exp == 0 { + return (1,false); + } + let mut base = self; + let mut acc: Self = 1; + let mut overflown = false; + // Scratch space for storing results of overflowing_mul. + let mut r; + + while exp > 1 { + if (exp & 1) == 1 { + r = acc.overflowing_mul(base); + acc = r.0; + overflown |= r.1; + } + exp /= 2; + r = base.overflowing_mul(base); + base = r.0; overflown |= r.1; } - exp /= 2; - r = base.overflowing_mul(base); - base = r.0; - overflown |= r.1; - } - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - r = acc.overflowing_mul(base); - r.1 |= overflown; - r + // since exp!=0, finally the exp must be 1. + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary and may cause a + // needless overflow. + r = acc.overflowing_mul(base); + r.1 |= overflown; + r + } } /// Raises self to the power of `exp`, using exponentiation by squaring. @@ -2608,28 +2704,68 @@ macro_rules! int_impl { #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] + #[rustc_allow_const_fn_unstable(is_val_statically_known, const_int_unchecked_arith)] #[inline] #[rustc_inherit_overflow_checks] + #[track_caller] // Hides the hackish overflow check for powers of two. pub const fn pow(self, mut exp: u32) -> Self { - if exp == 0 { - return 1; - } - let mut base = self; - let mut acc = 1; + // SAFETY: This path has the same behavior as the other. + if unsafe { intrinsics::is_val_statically_known(self) } + && self.unsigned_abs().is_power_of_two() + { + if self == 1 { // Avoid divide by zero + return 1; + } + if self == -1 { // Avoid divide by zero + return if exp & 1 != 0 { -1 } else { 1 }; + } + // SAFETY: We just checked this is a power of two. and above zero. + let power_used = unsafe { intrinsics::cttz_nonzero(self.wrapping_abs()) as u32 }; + if exp > Self::BITS / power_used { // Division of constants is free + #[allow(arithmetic_overflow)] + return Self::MAX * Self::MAX * 0; + } - while exp > 1 { - if (exp & 1) == 1 { - acc = acc * base; + // SAFETY: exp <= Self::BITS / power_used + let res = unsafe { intrinsics::unchecked_shl( + 1 as Self, + intrinsics::unchecked_mul(power_used, exp) as Self + )}; + // LLVM doesn't always optimize out the checks + // at the ir level. + + let sign = self.is_negative() && exp & 1 != 0; + #[allow(arithmetic_overflow)] + if !sign && res == Self::MIN { + // So it panics. + _ = Self::MAX * Self::MAX; + } + if sign { + res.wrapping_neg() + } else { + res + } + } else { + if exp == 0 { + return 1; + } + let mut base = self; + let mut acc = 1; + + while exp > 1 { + if (exp & 1) == 1 { + acc = acc * base; + } + exp /= 2; + base = base * base; } - exp /= 2; - base = base * base; - } - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - acc * base + // since exp!=0, finally the exp must be 1. + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary and may cause a + // needless overflow. + acc * base + } } /// Returns the square root of the number, rounded down. diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 640f1e3fa85..45ac544ceaa 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -288,6 +288,43 @@ macro_rules! nonzero_integer { unsafe { intrinsics::cttz_nonzero(self.get() as $UnsignedPrimitive) as u32 } } + /// Returns the number of ones in the binary representation of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(non_zero_count_ones)] + /// # fn main() { test().unwrap(); } + /// # fn test() -> Option<()> { + #[doc = concat!("# use std::num::{self, ", stringify!($Ty), "};")] + /// + /// let one = num::NonZeroU32::new(1)?; + /// let three = num::NonZeroU32::new(3)?; + #[doc = concat!("let a = ", stringify!($Ty), "::new(0b100_0000)?;")] + #[doc = concat!("let b = ", stringify!($Ty), "::new(0b100_0011)?;")] + /// + /// assert_eq!(a.count_ones(), one); + /// assert_eq!(b.count_ones(), three); + /// # Some(()) + /// # } + /// ``` + /// + #[unstable(feature = "non_zero_count_ones", issue = "120287")] + #[rustc_const_unstable(feature = "non_zero_count_ones", issue = "120287")] + #[doc(alias = "popcount")] + #[doc(alias = "popcnt")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub const fn count_ones(self) -> NonZeroU32 { + // SAFETY: + // `self` is non-zero, which means it has at least one bit set, which means + // that the result of `count_ones` is non-zero. + unsafe { NonZeroU32::new_unchecked(self.get().count_ones()) } + } + nonzero_integer_signedness_dependent_methods! { Self = $Ty, Primitive = $signedness $Int, diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index a217c2e259d..d450c68a5b2 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -1364,28 +1364,49 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] + #[rustc_allow_const_fn_unstable(is_val_statically_known, const_int_unchecked_arith)] #[inline] pub const fn checked_pow(self, mut exp: u32) -> Option<Self> { - if exp == 0 { - return Some(1); - } - let mut base = self; - let mut acc: Self = 1; - - while exp > 1 { - if (exp & 1) == 1 { - acc = try_opt!(acc.checked_mul(base)); + // SAFETY: This path has the same behavior as the other. + if unsafe { intrinsics::is_val_statically_known(self) } + && self.is_power_of_two() + { + if self == 1 { // Avoid divide by zero + return Some(1); + } + // SAFETY: We just checked this is a power of two. and above zero. + let power_used = unsafe { intrinsics::cttz_nonzero(self) as u32 }; + if exp > Self::BITS / power_used { return None; } // Division of constants is free + + // SAFETY: exp <= Self::BITS / power_used + unsafe { Some(intrinsics::unchecked_shl( + 1 as Self, + intrinsics::unchecked_mul(power_used, exp) as Self + )) } + // LLVM doesn't always optimize out the checks + // at the ir level. + } else { + if exp == 0 { + return Some(1); + } + let mut base = self; + let mut acc: Self = 1; + + while exp > 1 { + if (exp & 1) == 1 { + acc = try_opt!(acc.checked_mul(base)); + } + exp /= 2; + base = try_opt!(base.checked_mul(base)); } - exp /= 2; - base = try_opt!(base.checked_mul(base)); - } - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. + // since exp!=0, finally the exp must be 1. + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary and may cause a + // needless overflow. - acc.checked_mul(base) + acc.checked_mul(base) + } } /// Strict exponentiation. Computes `self.pow(exp)`, panicking if @@ -1887,27 +1908,48 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] + #[rustc_allow_const_fn_unstable(is_val_statically_known, const_int_unchecked_arith)] #[inline] pub const fn wrapping_pow(self, mut exp: u32) -> Self { - if exp == 0 { - return 1; - } - let mut base = self; - let mut acc: Self = 1; - - while exp > 1 { - if (exp & 1) == 1 { - acc = acc.wrapping_mul(base); + // SAFETY: This path has the same behavior as the other. + if unsafe { intrinsics::is_val_statically_known(self) } + && self.is_power_of_two() + { + if self == 1 { // Avoid divide by zero + return 1; + } + // SAFETY: We just checked this is a power of two. and above zero. + let power_used = unsafe { intrinsics::cttz_nonzero(self) as u32 }; + if exp > Self::BITS / power_used { return 0; } // Division of constants is free + + // SAFETY: exp <= Self::BITS / power_used + unsafe { intrinsics::unchecked_shl( + 1 as Self, + intrinsics::unchecked_mul(power_used, exp) as Self + )} + // LLVM doesn't always optimize out the checks + // at the ir level. + } else { + if exp == 0 { + return 1; + } + let mut base = self; + let mut acc: Self = 1; + + while exp > 1 { + if (exp & 1) == 1 { + acc = acc.wrapping_mul(base); + } + exp /= 2; + base = base.wrapping_mul(base); } - exp /= 2; - base = base.wrapping_mul(base); - } - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - acc.wrapping_mul(base) + // since exp!=0, finally the exp must be 1. + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary and may cause a + // needless overflow. + acc.wrapping_mul(base) + } } /// Calculates `self` + `rhs` @@ -2341,37 +2383,58 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] + #[rustc_allow_const_fn_unstable(is_val_statically_known, const_int_unchecked_arith)] #[inline] pub const fn overflowing_pow(self, mut exp: u32) -> (Self, bool) { - if exp == 0{ - return (1,false); - } - let mut base = self; - let mut acc: Self = 1; - let mut overflown = false; - // Scratch space for storing results of overflowing_mul. - let mut r; - - while exp > 1 { - if (exp & 1) == 1 { - r = acc.overflowing_mul(base); - acc = r.0; + // SAFETY: This path has the same behavior as the other. + if unsafe { intrinsics::is_val_statically_known(self) } + && self.is_power_of_two() + { + if self == 1 { // Avoid divide by zero + return (1, false); + } + // SAFETY: We just checked this is a power of two. and above zero. + let power_used = unsafe { intrinsics::cttz_nonzero(self) as u32 }; + if exp > Self::BITS / power_used { return (0, true); } // Division of constants is free + + // SAFETY: exp <= Self::BITS / power_used + unsafe { (intrinsics::unchecked_shl( + 1 as Self, + intrinsics::unchecked_mul(power_used, exp) as Self + ), false) } + // LLVM doesn't always optimize out the checks + // at the ir level. + } else { + if exp == 0{ + return (1,false); + } + let mut base = self; + let mut acc: Self = 1; + let mut overflown = false; + // Scratch space for storing results of overflowing_mul. + let mut r; + + while exp > 1 { + if (exp & 1) == 1 { + r = acc.overflowing_mul(base); + acc = r.0; + overflown |= r.1; + } + exp /= 2; + r = base.overflowing_mul(base); + base = r.0; overflown |= r.1; } - exp /= 2; - r = base.overflowing_mul(base); - base = r.0; - overflown |= r.1; - } - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - r = acc.overflowing_mul(base); - r.1 |= overflown; + // since exp!=0, finally the exp must be 1. + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary and may cause a + // needless overflow. + r = acc.overflowing_mul(base); + r.1 |= overflown; - r + r + } } /// Raises self to the power of `exp`, using exponentiation by squaring. @@ -2387,28 +2450,64 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] + #[rustc_allow_const_fn_unstable(is_val_statically_known, const_int_unchecked_arith)] #[inline] #[rustc_inherit_overflow_checks] + #[track_caller] // Hides the hackish overflow check for powers of two. pub const fn pow(self, mut exp: u32) -> Self { - if exp == 0 { - return 1; - } - let mut base = self; - let mut acc = 1; + // LLVM now knows that `self` is a constant value, but not a + // constant in Rust. This allows us to compute the power used at + // compile-time. + // + // This will likely add a branch in debug builds, but this should + // be ok. + // + // This is a massive performance boost in release builds as you can + // get the power of a power of two and the exponent through a `shl` + // instruction, but we must add a couple more checks for parity with + // our own `pow`. + // SAFETY: This path has the same behavior as the other. + if unsafe { intrinsics::is_val_statically_known(self) } + && self.is_power_of_two() + { + if self == 1 { // Avoid divide by zero + return 1; + } + // SAFETY: We just checked this is a power of two. and above zero. + let power_used = unsafe { intrinsics::cttz_nonzero(self) as u32 }; + if exp > Self::BITS / power_used { // Division of constants is free + #[allow(arithmetic_overflow)] + return Self::MAX * Self::MAX * 0; + } - while exp > 1 { - if (exp & 1) == 1 { - acc = acc * base; + // SAFETY: exp <= Self::BITS / power_used + unsafe { intrinsics::unchecked_shl( + 1 as Self, + intrinsics::unchecked_mul(power_used, exp) as Self + )} + // LLVM doesn't always optimize out the checks + // at the ir level. + } else { + if exp == 0 { + return 1; + } + let mut base = self; + let mut acc = 1; + + while exp > 1 { + if (exp & 1) == 1 { + acc = acc * base; + } + exp /= 2; + base = base * base; } - exp /= 2; - base = base * base; - } - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - acc * base + // since exp!=0, finally the exp must be 1. + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary and may cause a + // needless overflow. + acc * base + } } /// Returns the square root of the number, rounded down. diff --git a/library/core/src/ops/async_function.rs b/library/core/src/ops/async_function.rs new file mode 100644 index 00000000000..965873f163e --- /dev/null +++ b/library/core/src/ops/async_function.rs @@ -0,0 +1,108 @@ +use crate::future::Future; +use crate::marker::Tuple; + +/// An async-aware version of the [`Fn`](crate::ops::Fn) trait. +/// +/// All `async fn` and functions returning futures implement this trait. +#[unstable(feature = "async_fn_traits", issue = "none")] +#[rustc_paren_sugar] +#[fundamental] +#[must_use = "async closures are lazy and do nothing unless called"] +#[cfg_attr(not(bootstrap), lang = "async_fn")] +pub trait AsyncFn<Args: Tuple>: AsyncFnMut<Args> { + /// Future returned by [`AsyncFn::async_call`]. + #[unstable(feature = "async_fn_traits", issue = "none")] + type CallFuture<'a>: Future<Output = Self::Output> + where + Self: 'a; + + /// Call the [`AsyncFn`], returning a future which may borrow from the called closure. + #[unstable(feature = "async_fn_traits", issue = "none")] + extern "rust-call" fn async_call(&self, args: Args) -> Self::CallFuture<'_>; +} + +/// An async-aware version of the [`FnMut`](crate::ops::FnMut) trait. +/// +/// All `async fn` and functions returning futures implement this trait. +#[unstable(feature = "async_fn_traits", issue = "none")] +#[rustc_paren_sugar] +#[fundamental] +#[must_use = "async closures are lazy and do nothing unless called"] +#[cfg_attr(not(bootstrap), lang = "async_fn_mut")] +pub trait AsyncFnMut<Args: Tuple>: AsyncFnOnce<Args> { + /// Future returned by [`AsyncFnMut::async_call_mut`]. + #[unstable(feature = "async_fn_traits", issue = "none")] + type CallMutFuture<'a>: Future<Output = Self::Output> + where + Self: 'a; + + /// Call the [`AsyncFnMut`], returning a future which may borrow from the called closure. + #[unstable(feature = "async_fn_traits", issue = "none")] + extern "rust-call" fn async_call_mut(&mut self, args: Args) -> Self::CallMutFuture<'_>; +} + +/// An async-aware version of the [`FnOnce`](crate::ops::FnOnce) trait. +/// +/// All `async fn` and functions returning futures implement this trait. +#[unstable(feature = "async_fn_traits", issue = "none")] +#[rustc_paren_sugar] +#[fundamental] +#[must_use = "async closures are lazy and do nothing unless called"] +#[cfg_attr(not(bootstrap), lang = "async_fn_once")] +pub trait AsyncFnOnce<Args: Tuple> { + /// Future returned by [`AsyncFnOnce::async_call_once`]. + #[unstable(feature = "async_fn_traits", issue = "none")] + type CallOnceFuture: Future<Output = Self::Output>; + + /// Output type of the called closure's future. + #[unstable(feature = "async_fn_traits", issue = "none")] + type Output; + + /// Call the [`AsyncFnOnce`], returning a future which may move out of the called closure. + #[unstable(feature = "async_fn_traits", issue = "none")] + extern "rust-call" fn async_call_once(self, args: Args) -> Self::CallOnceFuture; +} + +mod impls { + use super::{AsyncFn, AsyncFnMut, AsyncFnOnce}; + use crate::future::Future; + use crate::marker::Tuple; + + #[unstable(feature = "async_fn_traits", issue = "none")] + impl<F: Fn<A>, A: Tuple> AsyncFn<A> for F + where + <F as FnOnce<A>>::Output: Future, + { + type CallFuture<'a> = <F as FnOnce<A>>::Output where Self: 'a; + + extern "rust-call" fn async_call(&self, args: A) -> Self::CallFuture<'_> { + self.call(args) + } + } + + #[unstable(feature = "async_fn_traits", issue = "none")] + impl<F: FnMut<A>, A: Tuple> AsyncFnMut<A> for F + where + <F as FnOnce<A>>::Output: Future, + { + type CallMutFuture<'a> = <F as FnOnce<A>>::Output where Self: 'a; + + extern "rust-call" fn async_call_mut(&mut self, args: A) -> Self::CallMutFuture<'_> { + self.call_mut(args) + } + } + + #[unstable(feature = "async_fn_traits", issue = "none")] + impl<F: FnOnce<A>, A: Tuple> AsyncFnOnce<A> for F + where + <F as FnOnce<A>>::Output: Future, + { + type CallOnceFuture = <F as FnOnce<A>>::Output; + + type Output = <<F as FnOnce<A>>::Output as Future>::Output; + + extern "rust-call" fn async_call_once(self, args: A) -> Self::CallOnceFuture { + self.call_once(args) + } + } +} diff --git a/library/core/src/ops/mod.rs b/library/core/src/ops/mod.rs index 35654d0b853..4289a86f89b 100644 --- a/library/core/src/ops/mod.rs +++ b/library/core/src/ops/mod.rs @@ -139,6 +139,7 @@ #![stable(feature = "rust1", since = "1.0.0")] mod arith; +mod async_function; mod bit; mod control_flow; mod coroutine; @@ -173,6 +174,9 @@ pub use self::drop::Drop; #[stable(feature = "rust1", since = "1.0.0")] pub use self::function::{Fn, FnMut, FnOnce}; +#[unstable(feature = "async_fn_traits", issue = "none")] +pub use self::async_function::{AsyncFn, AsyncFnMut, AsyncFnOnce}; + #[stable(feature = "rust1", since = "1.0.0")] pub use self::index::{Index, IndexMut}; diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs index 6e11b92b618..f03e0790305 100644 --- a/library/std/src/os/mod.rs +++ b/library/std/src/os/mod.rs @@ -85,6 +85,9 @@ pub mod linux; #[cfg(any(target_os = "wasi", doc))] pub mod wasi; +#[cfg(any(all(target_os = "wasi", target_env = "preview2"), doc))] +pub mod wasi_preview2; + // windows #[cfg(not(all( doc, diff --git a/library/std/src/os/wasi/mod.rs b/library/std/src/os/wasi/mod.rs index bbaf328f457..05c8d30073f 100644 --- a/library/std/src/os/wasi/mod.rs +++ b/library/std/src/os/wasi/mod.rs @@ -28,7 +28,8 @@ //! [`OsStr`]: crate::ffi::OsStr //! [`OsString`]: crate::ffi::OsString -#![stable(feature = "rust1", since = "1.0.0")] +#![cfg_attr(not(target_env = "preview2"), stable(feature = "rust1", since = "1.0.0"))] +#![cfg_attr(target_env = "preview2", unstable(feature = "wasm_preview2", issue = "none"))] #![deny(unsafe_op_in_unsafe_fn)] #![doc(cfg(target_os = "wasi"))] diff --git a/library/std/src/os/wasi_preview2/mod.rs b/library/std/src/os/wasi_preview2/mod.rs new file mode 100644 index 00000000000..1d44dd72814 --- /dev/null +++ b/library/std/src/os/wasi_preview2/mod.rs @@ -0,0 +1,5 @@ +//! Platform-specific extensions to `std` for Preview 2 of the WebAssembly System Interface (WASI). +//! +//! This module is currently empty, but will be filled over time as wasi-libc support for WASI Preview 2 is stabilized. + +#![stable(feature = "raw_ext", since = "1.1.0")] diff --git a/library/std/src/sys/pal/mod.rs b/library/std/src/sys/pal/mod.rs index 041b7c35582..f927d88d46c 100644 --- a/library/std/src/sys/pal/mod.rs +++ b/library/std/src/sys/pal/mod.rs @@ -40,6 +40,9 @@ cfg_if::cfg_if! { } else if #[cfg(target_os = "wasi")] { mod wasi; pub use self::wasi::*; + } else if #[cfg(all(target_os = "wasi", target_env = "preview2"))] { + mod wasi_preview2; + pub use self::wasi_preview2::*; } else if #[cfg(target_family = "wasm")] { mod wasm; pub use self::wasm::*; diff --git a/library/std/src/sys/pal/wasi/helpers.rs b/library/std/src/sys/pal/wasi/helpers.rs new file mode 100644 index 00000000000..82149cef8fa --- /dev/null +++ b/library/std/src/sys/pal/wasi/helpers.rs @@ -0,0 +1,123 @@ +use crate::io as std_io; +use crate::mem; + +#[inline] +pub fn is_interrupted(errno: i32) -> bool { + errno == wasi::ERRNO_INTR.raw().into() +} + +pub fn decode_error_kind(errno: i32) -> std_io::ErrorKind { + use std_io::ErrorKind; + + let Ok(errno) = u16::try_from(errno) else { + return ErrorKind::Uncategorized; + }; + + macro_rules! match_errno { + ($($($errno:ident)|+ => $errkind:ident),*, _ => $wildcard:ident $(,)?) => { + match errno { + $(e if $(e == ::wasi::$errno.raw())||+ => ErrorKind::$errkind),*, + _ => ErrorKind::$wildcard, + } + }; + } + + match_errno! { + ERRNO_2BIG => ArgumentListTooLong, + ERRNO_ACCES => PermissionDenied, + ERRNO_ADDRINUSE => AddrInUse, + ERRNO_ADDRNOTAVAIL => AddrNotAvailable, + ERRNO_AFNOSUPPORT => Unsupported, + ERRNO_AGAIN => WouldBlock, + // ALREADY => "connection already in progress", + // BADF => "bad file descriptor", + // BADMSG => "bad message", + ERRNO_BUSY => ResourceBusy, + // CANCELED => "operation canceled", + // CHILD => "no child processes", + ERRNO_CONNABORTED => ConnectionAborted, + ERRNO_CONNREFUSED => ConnectionRefused, + ERRNO_CONNRESET => ConnectionReset, + ERRNO_DEADLK => Deadlock, + // DESTADDRREQ => "destination address required", + ERRNO_DOM => InvalidInput, + // DQUOT => /* reserved */, + ERRNO_EXIST => AlreadyExists, + // FAULT => "bad address", + ERRNO_FBIG => FileTooLarge, + ERRNO_HOSTUNREACH => HostUnreachable, + // IDRM => "identifier removed", + // ILSEQ => "illegal byte sequence", + // INPROGRESS => "operation in progress", + ERRNO_INTR => Interrupted, + ERRNO_INVAL => InvalidInput, + ERRNO_IO => Uncategorized, + // ISCONN => "socket is connected", + ERRNO_ISDIR => IsADirectory, + ERRNO_LOOP => FilesystemLoop, + // MFILE => "file descriptor value too large", + ERRNO_MLINK => TooManyLinks, + // MSGSIZE => "message too large", + // MULTIHOP => /* reserved */, + ERRNO_NAMETOOLONG => InvalidFilename, + ERRNO_NETDOWN => NetworkDown, + // NETRESET => "connection aborted by network", + ERRNO_NETUNREACH => NetworkUnreachable, + // NFILE => "too many files open in system", + // NOBUFS => "no buffer space available", + ERRNO_NODEV => NotFound, + ERRNO_NOENT => NotFound, + // NOEXEC => "executable file format error", + // NOLCK => "no locks available", + // NOLINK => /* reserved */, + ERRNO_NOMEM => OutOfMemory, + // NOMSG => "no message of the desired type", + // NOPROTOOPT => "protocol not available", + ERRNO_NOSPC => StorageFull, + ERRNO_NOSYS => Unsupported, + ERRNO_NOTCONN => NotConnected, + ERRNO_NOTDIR => NotADirectory, + ERRNO_NOTEMPTY => DirectoryNotEmpty, + // NOTRECOVERABLE => "state not recoverable", + // NOTSOCK => "not a socket", + ERRNO_NOTSUP => Unsupported, + // NOTTY => "inappropriate I/O control operation", + ERRNO_NXIO => NotFound, + // OVERFLOW => "value too large to be stored in data type", + // OWNERDEAD => "previous owner died", + ERRNO_PERM => PermissionDenied, + ERRNO_PIPE => BrokenPipe, + // PROTO => "protocol error", + ERRNO_PROTONOSUPPORT => Unsupported, + // PROTOTYPE => "protocol wrong type for socket", + // RANGE => "result too large", + ERRNO_ROFS => ReadOnlyFilesystem, + ERRNO_SPIPE => NotSeekable, + ERRNO_SRCH => NotFound, + // STALE => /* reserved */, + ERRNO_TIMEDOUT => TimedOut, + ERRNO_TXTBSY => ResourceBusy, + ERRNO_XDEV => CrossesDevices, + ERRNO_NOTCAPABLE => PermissionDenied, + _ => Uncategorized, + } +} + +pub fn abort_internal() -> ! { + unsafe { libc::abort() } +} + +pub fn hashmap_random_keys() -> (u64, u64) { + let mut ret = (0u64, 0u64); + unsafe { + let base = &mut ret as *mut (u64, u64) as *mut u8; + let len = mem::size_of_val(&ret); + wasi::random_get(base, len).expect("random_get failure"); + } + return ret; +} + +#[inline] +pub(crate) fn err2io(err: wasi::Errno) -> std_io::Error { + std_io::Error::from_raw_os_error(err.raw().into()) +} diff --git a/library/std/src/sys/pal/wasi/mod.rs b/library/std/src/sys/pal/wasi/mod.rs index 4ffc8ecdd67..a4b55093bf4 100644 --- a/library/std/src/sys/pal/wasi/mod.rs +++ b/library/std/src/sys/pal/wasi/mod.rs @@ -14,9 +14,6 @@ //! compiling for wasm. That way it's a compile time error for something that's //! guaranteed to be a runtime error! -use crate::io as std_io; -use crate::mem; - #[path = "../unix/alloc.rs"] pub mod alloc; pub mod args; @@ -72,123 +69,12 @@ cfg_if::cfg_if! { mod common; pub use common::*; -#[inline] -pub fn is_interrupted(errno: i32) -> bool { - errno == wasi::ERRNO_INTR.raw().into() -} - -pub fn decode_error_kind(errno: i32) -> std_io::ErrorKind { - use std_io::ErrorKind; - - let Ok(errno) = u16::try_from(errno) else { - return ErrorKind::Uncategorized; - }; - - macro_rules! match_errno { - ($($($errno:ident)|+ => $errkind:ident),*, _ => $wildcard:ident $(,)?) => { - match errno { - $(e if $(e == ::wasi::$errno.raw())||+ => ErrorKind::$errkind),*, - _ => ErrorKind::$wildcard, - } - }; - } - - match_errno! { - ERRNO_2BIG => ArgumentListTooLong, - ERRNO_ACCES => PermissionDenied, - ERRNO_ADDRINUSE => AddrInUse, - ERRNO_ADDRNOTAVAIL => AddrNotAvailable, - ERRNO_AFNOSUPPORT => Unsupported, - ERRNO_AGAIN => WouldBlock, - // ALREADY => "connection already in progress", - // BADF => "bad file descriptor", - // BADMSG => "bad message", - ERRNO_BUSY => ResourceBusy, - // CANCELED => "operation canceled", - // CHILD => "no child processes", - ERRNO_CONNABORTED => ConnectionAborted, - ERRNO_CONNREFUSED => ConnectionRefused, - ERRNO_CONNRESET => ConnectionReset, - ERRNO_DEADLK => Deadlock, - // DESTADDRREQ => "destination address required", - ERRNO_DOM => InvalidInput, - // DQUOT => /* reserved */, - ERRNO_EXIST => AlreadyExists, - // FAULT => "bad address", - ERRNO_FBIG => FileTooLarge, - ERRNO_HOSTUNREACH => HostUnreachable, - // IDRM => "identifier removed", - // ILSEQ => "illegal byte sequence", - // INPROGRESS => "operation in progress", - ERRNO_INTR => Interrupted, - ERRNO_INVAL => InvalidInput, - ERRNO_IO => Uncategorized, - // ISCONN => "socket is connected", - ERRNO_ISDIR => IsADirectory, - ERRNO_LOOP => FilesystemLoop, - // MFILE => "file descriptor value too large", - ERRNO_MLINK => TooManyLinks, - // MSGSIZE => "message too large", - // MULTIHOP => /* reserved */, - ERRNO_NAMETOOLONG => InvalidFilename, - ERRNO_NETDOWN => NetworkDown, - // NETRESET => "connection aborted by network", - ERRNO_NETUNREACH => NetworkUnreachable, - // NFILE => "too many files open in system", - // NOBUFS => "no buffer space available", - ERRNO_NODEV => NotFound, - ERRNO_NOENT => NotFound, - // NOEXEC => "executable file format error", - // NOLCK => "no locks available", - // NOLINK => /* reserved */, - ERRNO_NOMEM => OutOfMemory, - // NOMSG => "no message of the desired type", - // NOPROTOOPT => "protocol not available", - ERRNO_NOSPC => StorageFull, - ERRNO_NOSYS => Unsupported, - ERRNO_NOTCONN => NotConnected, - ERRNO_NOTDIR => NotADirectory, - ERRNO_NOTEMPTY => DirectoryNotEmpty, - // NOTRECOVERABLE => "state not recoverable", - // NOTSOCK => "not a socket", - ERRNO_NOTSUP => Unsupported, - // NOTTY => "inappropriate I/O control operation", - ERRNO_NXIO => NotFound, - // OVERFLOW => "value too large to be stored in data type", - // OWNERDEAD => "previous owner died", - ERRNO_PERM => PermissionDenied, - ERRNO_PIPE => BrokenPipe, - // PROTO => "protocol error", - ERRNO_PROTONOSUPPORT => Unsupported, - // PROTOTYPE => "protocol wrong type for socket", - // RANGE => "result too large", - ERRNO_ROFS => ReadOnlyFilesystem, - ERRNO_SPIPE => NotSeekable, - ERRNO_SRCH => NotFound, - // STALE => /* reserved */, - ERRNO_TIMEDOUT => TimedOut, - ERRNO_TXTBSY => ResourceBusy, - ERRNO_XDEV => CrossesDevices, - ERRNO_NOTCAPABLE => PermissionDenied, - _ => Uncategorized, - } -} - -pub fn abort_internal() -> ! { - unsafe { libc::abort() } -} - -pub fn hashmap_random_keys() -> (u64, u64) { - let mut ret = (0u64, 0u64); - unsafe { - let base = &mut ret as *mut (u64, u64) as *mut u8; - let len = mem::size_of_val(&ret); - wasi::random_get(base, len).expect("random_get failure"); - } - return ret; -} - -#[inline] -fn err2io(err: wasi::Errno) -> std_io::Error { - std_io::Error::from_raw_os_error(err.raw().into()) -} +mod helpers; +// These exports are listed individually to work around Rust's glob import +// conflict rules. If we glob export `helpers` and `common` together, then +// the compiler complains about conflicts. +pub use helpers::abort_internal; +pub use helpers::decode_error_kind; +use helpers::err2io; +pub use helpers::hashmap_random_keys; +pub use helpers::is_interrupted; diff --git a/library/std/src/sys/pal/wasi_preview2/mod.rs b/library/std/src/sys/pal/wasi_preview2/mod.rs new file mode 100644 index 00000000000..b61695015bb --- /dev/null +++ b/library/std/src/sys/pal/wasi_preview2/mod.rs @@ -0,0 +1,78 @@ +//! System bindings for the wasi preview 2 target. +//! +//! This is the next evolution of the original wasi target, and is intended to +//! replace that target over time. +//! +//! To begin with, this target mirrors the wasi target 1 to 1, but over +//! time this will change significantly. + +#[path = "../unix/alloc.rs"] +pub mod alloc; +#[path = "../wasi/args.rs"] +pub mod args; +#[path = "../unix/cmath.rs"] +pub mod cmath; +#[path = "../wasi/env.rs"] +pub mod env; +#[path = "../wasi/fd.rs"] +pub mod fd; +#[path = "../wasi/fs.rs"] +pub mod fs; +#[allow(unused)] +#[path = "../wasm/atomics/futex.rs"] +pub mod futex; +#[path = "../wasi/io.rs"] +pub mod io; + +#[path = "../wasi/net.rs"] +pub mod net; +#[path = "../wasi/os.rs"] +pub mod os; +#[path = "../unix/os_str.rs"] +pub mod os_str; +#[path = "../unix/path.rs"] +pub mod path; +#[path = "../unsupported/pipe.rs"] +pub mod pipe; +#[path = "../unsupported/process.rs"] +pub mod process; +#[path = "../wasi/stdio.rs"] +pub mod stdio; +#[path = "../wasi/thread.rs"] +pub mod thread; +#[path = "../unsupported/thread_local_dtor.rs"] +pub mod thread_local_dtor; +#[path = "../unsupported/thread_local_key.rs"] +pub mod thread_local_key; +#[path = "../wasi/time.rs"] +pub mod time; + +cfg_if::cfg_if! { + if #[cfg(target_feature = "atomics")] { + compile_error!("The wasm32-wasi-preview2 target does not support atomics"); + } else { + #[path = "../unsupported/locks/mod.rs"] + pub mod locks; + #[path = "../unsupported/once.rs"] + pub mod once; + #[path = "../unsupported/thread_parking.rs"] + pub mod thread_parking; + } +} + +#[path = "../unsupported/common.rs"] +#[deny(unsafe_op_in_unsafe_fn)] +#[allow(unused)] +mod common; +pub use common::*; + +#[path = "../wasi/helpers.rs"] +mod helpers; +// These exports are listed individually to work around Rust's glob import +// conflict rules. If we glob export `helpers` and `common` together, then +// the compiler complains about conflicts. +pub use helpers::abort_internal; +pub use helpers::decode_error_kind; +use helpers::err2io; +pub use helpers::hashmap_random_keys; +pub use helpers::is_interrupted; diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index f954f01fe4e..e06bc3fb09a 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -367,10 +367,13 @@ fn copy_self_contained_objects( let srcdir = builder .wasi_root(target) .unwrap_or_else(|| { - panic!("Target {:?} does not have a \"wasi-root\" key", target.triple) + panic!( + "Target {:?} does not have a \"wasi-root\" key in Config.toml", + target.triple + ) }) .join("lib") - .join(target.to_string().replace("-preview1", "")); + .join(target.to_string().replace("-preview1", "").replace("-preview2", "")); for &obj in &["libc.a", "crt1-command.o", "crt1-reactor.o"] { copy_and_stamp( builder, diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 1336abf6c7a..1726e7aacbc 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -88,7 +88,7 @@ const EXTRA_CHECK_CFGS: &[(Option<Mode>, &str, Option<&[&'static str]>)] = &[ (Some(Mode::Std), "no_sync", None), (Some(Mode::Std), "backtrace_in_libstd", None), /* Extra values not defined in the built-in targets yet, but used in std */ - (Some(Mode::Std), "target_env", Some(&["libnx"])), + (Some(Mode::Std), "target_env", Some(&["libnx", "preview2"])), // (Some(Mode::Std), "target_os", Some(&[])), // #[cfg(bootstrap)] zkvm (Some(Mode::Std), "target_os", Some(&["zkvm"])), diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index 1998b008dc8..990998ea704 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -59,6 +59,7 @@ - [*-unknown-openbsd](platform-support/openbsd.md) - [\*-unknown-uefi](platform-support/unknown-uefi.md) - [wasm32-wasi-preview1-threads](platform-support/wasm32-wasi-preview1-threads.md) + - [wasm32-wasi-preview2](platform-support/wasm32-wasi-preview2.md) - [wasm64-unknown-unknown](platform-support/wasm64-unknown-unknown.md) - [\*-win7-windows-msvc](platform-support/win7-windows-msvc.md) - [x86_64-fortanix-unknown-sgx](platform-support/x86_64-fortanix-unknown-sgx.md) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index f648a60b6c4..fb751b7229e 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -360,6 +360,7 @@ target | std | host | notes `thumbv7a-pc-windows-msvc` | ? | | `thumbv7a-uwp-windows-msvc` | ✓ | | `thumbv7neon-unknown-linux-musleabihf` | ? | | Thumb2-mode ARMv7-A Linux with NEON, MUSL +[`wasm32-wasi-preview2`](platform-support/wasm32-wasi-preview2.md) | ✓ | | WebAssembly [`wasm64-unknown-unknown`](platform-support/wasm64-unknown-unknown.md) | ? | | WebAssembly `x86_64-apple-ios-macabi` | ✓ | | Apple Catalyst on x86_64 [`x86_64-apple-tvos`](platform-support/apple-tvos.md) | ? | | x86 64-bit tvOS diff --git a/src/doc/rustc/src/platform-support/wasm32-wasi-preview2.md b/src/doc/rustc/src/platform-support/wasm32-wasi-preview2.md new file mode 100644 index 00000000000..837efd13d41 --- /dev/null +++ b/src/doc/rustc/src/platform-support/wasm32-wasi-preview2.md @@ -0,0 +1,30 @@ +# `wasm32-wasi-preview2` + +**Tier: 3** + +The `wasm32-wasi-preview2` target is a new and still (as of January 2024) an +experimental target. This target is an extension to `wasm32-wasi-preview1` target, +originally known as `wasm32-wasi`. It is the next evolution in the development of +wasi (the [WebAssembly System Interface](https://wasi.dev)) that uses the WebAssembly +[component model] to allow for a standardized set of syscalls that are intended to empower +WebAssembly binaries with native host capabilities. + +[component model]: https://github.com/WebAssembly/component-model + +## Target maintainers + +- Alex Crichton, https://github.com/alexcrichton +- Ryan Levick, https://github.com/rylev + +## Requirements + +This target is cross-compiled. The target supports `std` fully. + +## Platform requirements + +The WebAssembly runtime should support the wasi preview 2 API set. + +This target is not a stable target. This means that there are only a few engines +which implement wasi preview 2, for example: + +* Wasmtime - `-W component-model` diff --git a/src/doc/style-guide/src/editions.md b/src/doc/style-guide/src/editions.md index 19e62c4867c..b9a89c20cee 100644 --- a/src/doc/style-guide/src/editions.md +++ b/src/doc/style-guide/src/editions.md @@ -36,6 +36,10 @@ For a full history of changes in the Rust 2024 style edition, see the git history of the style guide. Notable changes in the Rust 2024 style edition include: +- [#114764](https://github.com/rust-lang/rust/pull/114764) As the last member + of a delimited expression, delimited expressions are generally combinable, + regardless of the number of members. Previously only applied with exactly + one member (except for closures with explicit blocks). - Miscellaneous `rustfmt` bugfixes. - Use version-sort (sort `x8`, `x16`, `x32`, `x64`, `x128` in that order). - Change "ASCIIbetical" sort to Unicode-aware "non-lowercase before lowercase". diff --git a/src/doc/style-guide/src/expressions.md b/src/doc/style-guide/src/expressions.md index 12037b5992e..171a24cd89d 100644 --- a/src/doc/style-guide/src/expressions.md +++ b/src/doc/style-guide/src/expressions.md @@ -818,11 +818,11 @@ E.g., `&&Some(foo)` matches, `Foo(4, Bar)` does not. ## Combinable expressions -Where a function call has a single argument, and that argument is formatted -across multiple-lines, format the outer call as if it were a single-line call, +When the last argument in a function call is formatted across +multiple-lines, format the outer call as if it were a single-line call, if the result fits. Apply the same combining behaviour to any similar expressions which have multi-line, block-indented lists of sub-expressions -delimited by parentheses (e.g., macros or tuple struct literals). E.g., +delimited by parentheses, brackets, or braces. E.g., ```rust foo(bar( @@ -848,20 +848,61 @@ let arr = [combinable( an_expr, another_expr, )]; + +let x = Thing(an_expr, another_expr, match cond { + A => 1, + B => 2, +}); + +let x = format!("Stuff: {}", [ + an_expr, + another_expr, +]); + +let x = func(an_expr, another_expr, SomeStruct { + field: this_is_long, + another_field: 123, +}); ``` Apply this behavior recursively. -For a function with multiple arguments, if the last argument is a multi-line -closure with an explicit block, there are no other closure arguments, and all -the arguments and the first line of the closure fit on the first line, use the -same combining behavior: +If the last argument is a multi-line closure with an explicit block, +only apply the combining behavior if there are no other closure arguments. ```rust +// Combinable foo(first_arg, x, |param| { action(); foo(param) }) +// Not combinable, because the closure is not the last argument +foo( + first_arg, + |param| { + action(); + foo(param) + }, + whatever, +) +// Not combinable, because the first line of the closure does not fit +foo( + first_arg, + x, + move |very_long_param_causing_line_to_overflow| -> Bar { + action(); + foo(param) + }, +) +// Not combinable, because there is more than one closure argument +foo( + first_arg, + |x| x.bar(), + |param| { + action(); + foo(param) + }, +) ``` ## Ranges diff --git a/src/tools/clippy/tests/ui/modulo_one.rs b/src/tools/clippy/tests/ui/modulo_one.rs index c1dbe9d9a87..c332a15f157 100644 --- a/src/tools/clippy/tests/ui/modulo_one.rs +++ b/src/tools/clippy/tests/ui/modulo_one.rs @@ -33,7 +33,6 @@ fn main() { INT_MIN % NEG_ONE; //~^ ERROR: this operation will panic at runtime //~| ERROR: any number modulo -1 will panic/overflow or result in 0 - // ONLY caught by rustc + // Not caught by lint, we don't look into static items, even if entirely immutable. INT_MIN % STATIC_NEG_ONE; - //~^ ERROR: this operation will panic at runtime } diff --git a/src/tools/clippy/tests/ui/modulo_one.stderr b/src/tools/clippy/tests/ui/modulo_one.stderr index cc211ab6cd3..06bbb0f5d9a 100644 --- a/src/tools/clippy/tests/ui/modulo_one.stderr +++ b/src/tools/clippy/tests/ui/modulo_one.stderr @@ -12,12 +12,6 @@ error: this operation will panic at runtime LL | INT_MIN % NEG_ONE; | ^^^^^^^^^^^^^^^^^ attempt to compute `i64::MIN % -1_i64`, which would overflow -error: this operation will panic at runtime - --> $DIR/modulo_one.rs:37:5 - | -LL | INT_MIN % STATIC_NEG_ONE; - | ^^^^^^^^^^^^^^^^^^^^^^^^ attempt to compute `i64::MIN % -1_i64`, which would overflow - error: any number modulo 1 will be 0 --> $DIR/modulo_one.rs:8:5 | @@ -57,5 +51,5 @@ error: any number modulo -1 will panic/overflow or result in 0 LL | INT_MIN % NEG_ONE; | ^^^^^^^^^^^^^^^^^ -error: aborting due to 9 previous errors +error: aborting due to 8 previous errors diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index e70e01e8757..ff907152ca9 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -1109,9 +1109,6 @@ fn ignore_lldb(config: &Config, line: &str) -> IgnoreDecision { } fn ignore_llvm(config: &Config, line: &str) -> IgnoreDecision { - if config.system_llvm && line.starts_with("no-system-llvm") { - return IgnoreDecision::Ignore { reason: "ignored when the system LLVM is used".into() }; - } if let Some(needed_components) = config.parse_name_value_directive(line, "needs-llvm-components") { diff --git a/src/tools/compiletest/src/header/tests.rs b/src/tools/compiletest/src/header/tests.rs index 8882f1582ac..c859e8acade 100644 --- a/src/tools/compiletest/src/header/tests.rs +++ b/src/tools/compiletest/src/header/tests.rs @@ -243,15 +243,6 @@ fn aux_build() { } #[test] -fn no_system_llvm() { - let config: Config = cfg().system_llvm(false).build(); - assert!(!check_ignore(&config, "// no-system-llvm")); - - let config: Config = cfg().system_llvm(true).build(); - assert!(check_ignore(&config, "// no-system-llvm")); -} - -#[test] fn llvm_version() { let config: Config = cfg().llvm_version("8.1.2").build(); assert!(check_ignore(&config, "// min-llvm-version: 9.0")); @@ -267,6 +258,18 @@ fn llvm_version() { } #[test] +fn system_llvm_version() { + let config: Config = cfg().system_llvm(true).llvm_version("17.0.0").build(); + assert!(check_ignore(&config, "// min-system-llvm-version: 18.0")); + + let config: Config = cfg().system_llvm(true).llvm_version("18.0.0").build(); + assert!(!check_ignore(&config, "// min-system-llvm-version: 18.0")); + + let config: Config = cfg().llvm_version("17.0.0").build(); + assert!(!check_ignore(&config, "// min-system-llvm-version: 18.0")); +} + +#[test] fn ignore_target() { let config: Config = cfg().target("x86_64-unknown-linux-gnu").build(); diff --git a/src/tools/miri/src/shims/intrinsics/mod.rs b/src/tools/miri/src/shims/intrinsics/mod.rs index a1db7bf74f2..8edc0a4220d 100644 --- a/src/tools/miri/src/shims/intrinsics/mod.rs +++ b/src/tools/miri/src/shims/intrinsics/mod.rs @@ -5,6 +5,7 @@ use std::iter; use log::trace; +use rand::Rng; use rustc_apfloat::{Float, Round}; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::{ @@ -141,6 +142,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.write_pointer(Pointer::new(ptr.provenance, masked_addr), dest)?; } + // We want to return either `true` or `false` at random, or else something like + // ``` + // if !is_val_statically_known(0) { unreachable_unchecked(); } + // ``` + // Would not be considered UB, or the other way around (`is_val_statically_known(0)`). + "is_val_statically_known" => { + let [_] = check_arg_count(args)?; + let branch: bool = this.machine.rng.get_mut().gen(); + this.write_scalar(Scalar::from_bool(branch), dest)?; + } + // Floating-point operations "fabsf32" => { let [f] = check_arg_count(args)?; diff --git a/src/tools/miri/tests/pass/intrinsics.rs b/src/tools/miri/tests/pass/intrinsics.rs index 8c6eeab2219..8e46bd7ad48 100644 --- a/src/tools/miri/tests/pass/intrinsics.rs +++ b/src/tools/miri/tests/pass/intrinsics.rs @@ -33,6 +33,21 @@ fn main() { assert_eq!(intrinsics::likely(false), false); assert_eq!(intrinsics::unlikely(true), true); + let mut saw_true = false; + let mut saw_false = false; + + for _ in 0..50 { + if unsafe { intrinsics::is_val_statically_known(0) } { + saw_true = true; + } else { + saw_false = true; + } + } + assert!( + saw_true && saw_false, + "`is_val_statically_known` failed to return both true and false. Congrats, you won the lottery!" + ); + intrinsics::forget(Bomb); let _v = intrinsics::discriminant_value(&Some(())); diff --git a/tests/assembly/targets/targets-elf.rs b/tests/assembly/targets/targets-elf.rs index 6eec05e85ac..f6f2f5e88ff 100644 --- a/tests/assembly/targets/targets-elf.rs +++ b/tests/assembly/targets/targets-elf.rs @@ -534,6 +534,9 @@ // revisions: wasm64_unknown_unknown // [wasm64_unknown_unknown] compile-flags: --target wasm64-unknown-unknown // [wasm64_unknown_unknown] needs-llvm-components: webassembly +// revisions: wasm32_wasi_preview2 +// [wasm32_wasi_preview2] compile-flags: --target wasm32-wasi-preview2 +// [wasm32_wasi_preview2] needs-llvm-components: webassembly // revisions: x86_64_apple_darwin // [x86_64_apple_darwin] compile-flags: --target x86_64-apple-darwin // [x86_64_apple_darwin] needs-llvm-components: x86 diff --git a/tests/codegen/alloc-optimisation.rs b/tests/codegen/alloc-optimisation.rs index f88d695d87e..900eb687a45 100644 --- a/tests/codegen/alloc-optimisation.rs +++ b/tests/codegen/alloc-optimisation.rs @@ -1,5 +1,3 @@ -// -// no-system-llvm // compile-flags: -O #![crate_type = "lib"] diff --git a/tests/codegen/array-map.rs b/tests/codegen/array-map.rs index 4d218e6a951..9846cc7f5c8 100644 --- a/tests/codegen/array-map.rs +++ b/tests/codegen/array-map.rs @@ -1,5 +1,4 @@ // compile-flags: -C opt-level=3 -C target-cpu=x86-64-v3 -// no-system-llvm // only-x86_64 // ignore-debug (the extra assertions get in the way) @@ -10,7 +9,7 @@ pub fn short_integer_map(x: [u32; 8]) -> [u32; 8] { // CHECK: load <8 x i32> // CHECK: shl <8 x i32> - // CHECK: or <8 x i32> + // CHECK: or{{( disjoint)?}} <8 x i32> // CHECK: store <8 x i32> x.map(|x| 2 * x + 1) } diff --git a/tests/codegen/dealloc-no-unwind.rs b/tests/codegen/dealloc-no-unwind.rs index 3812ef44ff2..c2656908f16 100644 --- a/tests/codegen/dealloc-no-unwind.rs +++ b/tests/codegen/dealloc-no-unwind.rs @@ -1,4 +1,3 @@ -// no-system-llvm // compile-flags: -O #![crate_type="lib"] diff --git a/tests/codegen/fewer-names.rs b/tests/codegen/fewer-names.rs index df1080bff2b..05643fab96a 100644 --- a/tests/codegen/fewer-names.rs +++ b/tests/codegen/fewer-names.rs @@ -1,4 +1,3 @@ -// no-system-llvm // compile-flags: -Coverflow-checks=no -O // revisions: YES NO // [YES]compile-flags: -Zfewer-names=yes diff --git a/tests/codegen/integer-overflow.rs b/tests/codegen/integer-overflow.rs index 183de56db96..b5c351b5e35 100644 --- a/tests/codegen/integer-overflow.rs +++ b/tests/codegen/integer-overflow.rs @@ -1,4 +1,3 @@ -// no-system-llvm // compile-flags: -O -C overflow-checks=on #![crate_type = "lib"] diff --git a/tests/codegen/is_val_statically_known.rs b/tests/codegen/is_val_statically_known.rs new file mode 100644 index 00000000000..44187d4f667 --- /dev/null +++ b/tests/codegen/is_val_statically_known.rs @@ -0,0 +1,48 @@ +// compile-flags: --crate-type=lib -Zmerge-functions=disabled -O + +#![feature(core_intrinsics)] + +use std::intrinsics::is_val_statically_known; + +pub struct A(u32); +pub enum B { + Ye(u32), +} + +#[inline] +pub fn _u32(a: u32) -> i32 { + if unsafe { is_val_statically_known(a) } { 1 } else { 0 } +} + +// CHECK-LABEL: @_u32_true( +#[no_mangle] +pub fn _u32_true() -> i32 { + // CHECK: ret i32 1 + _u32(1) +} + +// CHECK-LABEL: @_u32_false( +#[no_mangle] +pub fn _u32_false(a: u32) -> i32 { + // CHECK: ret i32 0 + _u32(a) +} + +#[inline] +pub fn _bool(b: bool) -> i32 { + if unsafe { is_val_statically_known(b) } { 3 } else { 2 } +} + +// CHECK-LABEL: @_bool_true( +#[no_mangle] +pub fn _bool_true() -> i32 { + // CHECK: ret i32 3 + _bool(true) +} + +// CHECK-LABEL: @_bool_false( +#[no_mangle] +pub fn _bool_false(b: bool) -> i32 { + // CHECK: ret i32 2 + _bool(b) +} diff --git a/tests/codegen/issues/issue-116878.rs b/tests/codegen/issues/issue-116878.rs index d5f679459f7..5864f532324 100644 --- a/tests/codegen/issues/issue-116878.rs +++ b/tests/codegen/issues/issue-116878.rs @@ -1,4 +1,3 @@ -// no-system-llvm // compile-flags: -O // ignore-debug: the debug assertions get in the way #![crate_type = "lib"] diff --git a/tests/codegen/issues/issue-44056-macos-tls-align.rs b/tests/codegen/issues/issue-44056-macos-tls-align.rs index 1a3923f1bb1..44aa9766d3c 100644 --- a/tests/codegen/issues/issue-44056-macos-tls-align.rs +++ b/tests/codegen/issues/issue-44056-macos-tls-align.rs @@ -1,6 +1,5 @@ // // only-macos -// no-system-llvm // compile-flags: -O #![crate_type = "rlib"] diff --git a/tests/codegen/issues/issue-69101-bounds-check.rs b/tests/codegen/issues/issue-69101-bounds-check.rs index a3aca3a2912..655de45fd51 100644 --- a/tests/codegen/issues/issue-69101-bounds-check.rs +++ b/tests/codegen/issues/issue-69101-bounds-check.rs @@ -1,4 +1,3 @@ -// no-system-llvm // compile-flags: -O // ignore-debug: the debug assertions get in the way #![crate_type = "lib"] diff --git a/tests/codegen/match-optimizes-away.rs b/tests/codegen/match-optimizes-away.rs index 8f66c518ccf..400606b42d5 100644 --- a/tests/codegen/match-optimizes-away.rs +++ b/tests/codegen/match-optimizes-away.rs @@ -1,5 +1,4 @@ // -// no-system-llvm // compile-flags: -O #![crate_type="lib"] diff --git a/tests/codegen/pow_of_two.rs b/tests/codegen/pow_of_two.rs new file mode 100644 index 00000000000..a8c0550e332 --- /dev/null +++ b/tests/codegen/pow_of_two.rs @@ -0,0 +1,55 @@ +// compile-flags: --crate-type=lib -Zmerge-functions=disabled -O -C overflow-checks=false + +// CHECK-LABEL: @a( +#[no_mangle] +pub fn a(exp: u32) -> u64 { + // CHECK: %{{[^ ]+}} = icmp ugt i32 %exp, 64 + // CHECK: %{{[^ ]+}} = zext i32 %exp to i64 + // CHECK: %{{[^ ]+}} = shl nuw i64 {{[^ ]+}}, %{{[^ ]+}} + // CHECK: ret i64 %{{[^ ]+}} + 2u64.pow(exp) +} + +// CHECK-LABEL: @b( +#[no_mangle] +pub fn b(exp: u32) -> i64 { + // CHECK: %{{[^ ]+}} = icmp ugt i32 %exp, 64 + // CHECK: %{{[^ ]+}} = zext i32 %exp to i64 + // CHECK: %{{[^ ]+}} = shl nuw i64 {{[^ ]+}}, %{{[^ ]+}} + // CHECK: ret i64 %{{[^ ]+}} + 2i64.pow(exp) +} + +// CHECK-LABEL: @c( +#[no_mangle] +pub fn c(exp: u32) -> u32 { + // CHECK: %{{[^ ]+}} = icmp ugt i32 %exp, 16 + // CHECK: %{{[^ ]+}} = shl nuw nsw i32 %exp, 1 + // CHECK: %{{[^ ]+}} = shl nuw i32 1, %{{[^ ]+}} + // CHECK: %{{[^ ]+}} = select i1 %{{[^ ]+}}, i32 0, i32 %{{[^ ]+}} + // CHECK: ret i32 %{{[^ ]+}} + 4u32.pow(exp) +} + +// CHECK-LABEL: @d( +#[no_mangle] +pub fn d(exp: u32) -> u32 { + // CHECK: %{{[^ ]+}} = icmp ugt i32 %exp, 6 + // CHECK: %{{[^ ]+}} = mul nuw nsw i32 %exp, 5 + // CHECK: %{{[^ ]+}} = shl nuw nsw i32 1, %{{[^ ]+}} + // CHECK: %{{[^ ]+}} = select i1 {{[^ ]+}}, i32 0, i32 %{{[^ ]+}} + // CHECK: ret i32 %{{[^ ]+}} + 32u32.pow(exp) +} + +// CHECK-LABEL: @e( +#[no_mangle] +pub fn e(exp: u32) -> i32 { + // CHECK: %{{[^ ]+}} = icmp ugt i32 %exp, 6 + // CHECK: %{{[^ ]+}} = mul nuw {{(nsw )?}}i32 %exp, 5 + // CHECK: %{{[^ ]+}} = shl nuw {{(nsw )?}}i32 1, %{{[^ ]+}} + // CHECK: %{{[^ ]+}} = select i1 {{[^ ]+}}, i32 0, i32 %{{[^ ]+}} + // CHECK: ret i32 %{{[^ ]+}} + 32i32.pow(exp) +} +// note: d and e are expected to yield the same IR diff --git a/tests/codegen/ptr-read-metadata.rs b/tests/codegen/ptr-read-metadata.rs index 73d1db6df27..94152ed11ba 100644 --- a/tests/codegen/ptr-read-metadata.rs +++ b/tests/codegen/ptr-read-metadata.rs @@ -1,5 +1,4 @@ // compile-flags: -O -Z merge-functions=disabled -// no-system-llvm // ignore-debug (the extra assertions get in the way) #![crate_type = "lib"] diff --git a/tests/codegen/slice-as_chunks.rs b/tests/codegen/slice-as_chunks.rs index efac9f3d68d..e832f90d07a 100644 --- a/tests/codegen/slice-as_chunks.rs +++ b/tests/codegen/slice-as_chunks.rs @@ -1,4 +1,3 @@ -// no-system-llvm // compile-flags: -O // only-64bit (because the LLVM type of i64 for usize shows up) // ignore-debug: the debug assertions get in the way diff --git a/tests/codegen/slice-iter-len-eq-zero.rs b/tests/codegen/slice-iter-len-eq-zero.rs index c7515ce35a3..77febf5170d 100644 --- a/tests/codegen/slice-iter-len-eq-zero.rs +++ b/tests/codegen/slice-iter-len-eq-zero.rs @@ -1,4 +1,3 @@ -// no-system-llvm // compile-flags: -O // ignore-debug: the debug assertions add extra comparisons #![crate_type = "lib"] diff --git a/tests/codegen/slice-iter-nonnull.rs b/tests/codegen/slice-iter-nonnull.rs index 1e691cc34c9..93c8828ccd3 100644 --- a/tests/codegen/slice-iter-nonnull.rs +++ b/tests/codegen/slice-iter-nonnull.rs @@ -1,4 +1,3 @@ -// no-system-llvm // compile-flags: -O // ignore-debug (these add extra checks that make it hard to verify) #![crate_type = "lib"] diff --git a/tests/codegen/slice-position-bounds-check.rs b/tests/codegen/slice-position-bounds-check.rs index b494f42b296..57904e5e499 100644 --- a/tests/codegen/slice-position-bounds-check.rs +++ b/tests/codegen/slice-position-bounds-check.rs @@ -1,4 +1,3 @@ -// no-system-llvm // compile-flags: -O -C panic=abort #![crate_type = "lib"] diff --git a/tests/codegen/vec-iter-collect-len.rs b/tests/codegen/vec-iter-collect-len.rs index 73348ddd063..3a0d6c30919 100644 --- a/tests/codegen/vec-iter-collect-len.rs +++ b/tests/codegen/vec-iter-collect-len.rs @@ -1,5 +1,4 @@ // ignore-debug: the debug assertions get in the way -// no-system-llvm // compile-flags: -O #![crate_type="lib"] diff --git a/tests/codegen/vec-optimizes-away.rs b/tests/codegen/vec-optimizes-away.rs index 6f477a796b6..3be342dabeb 100644 --- a/tests/codegen/vec-optimizes-away.rs +++ b/tests/codegen/vec-optimizes-away.rs @@ -1,5 +1,4 @@ // ignore-debug: the debug assertions get in the way -// no-system-llvm // compile-flags: -O #![crate_type = "lib"] diff --git a/tests/coverage-run-rustdoc/doctest.coverage b/tests/coverage-run-rustdoc/doctest.coverage index 5797784f411..5125dc075ee 100644 --- a/tests/coverage-run-rustdoc/doctest.coverage +++ b/tests/coverage-run-rustdoc/doctest.coverage @@ -34,8 +34,7 @@ $DIR/doctest.rs: LL| |//! LL| |//! doctest returning a result: LL| 1|//! ``` - LL| 2|//! #[derive(Debug, PartialEq)] - ^1 + LL| 1|//! #[derive(Debug, PartialEq)] LL| 1|//! struct SomeError { LL| 1|//! msg: String, LL| 1|//! } @@ -63,7 +62,7 @@ $DIR/doctest.rs: LL| 1|//! println!("called some_func()"); LL| 1|//! } LL| |//! - LL| 0|//! #[derive(Debug)] + LL| |//! #[derive(Debug)] LL| |//! struct SomeError; LL| |//! LL| |//! extern crate doctest_crate; diff --git a/tests/coverage/bad_counter_ids.cov-map b/tests/coverage/bad_counter_ids.cov-map index 0b8081acfa6..3b51e3ef9de 100644 --- a/tests/coverage/bad_counter_ids.cov-map +++ b/tests/coverage/bad_counter_ids.cov-map @@ -1,19 +1,3 @@ -Function name: <bad_counter_ids::Foo as core::cmp::PartialEq>::eq -Raw bytes (9): 0x[01, 01, 00, 01, 01, 0c, 11, 00, 1a] -Number of files: 1 -- file 0 => global file 1 -Number of expressions: 0 -Number of file 0 mappings: 1 -- Code(Counter(0)) at (prev + 12, 17) to (start + 0, 26) - -Function name: <bad_counter_ids::Foo as core::fmt::Debug>::fmt -Raw bytes (9): 0x[01, 01, 00, 01, 01, 0c, 0a, 00, 0f] -Number of files: 1 -- file 0 => global file 1 -Number of expressions: 0 -Number of file 0 mappings: 1 -- Code(Counter(0)) at (prev + 12, 10) to (start + 0, 15) - Function name: bad_counter_ids::eq_bad Raw bytes (14): 0x[01, 01, 00, 02, 01, 23, 01, 02, 1f, 00, 03, 01, 00, 02] Number of files: 1 diff --git a/tests/coverage/bad_counter_ids.coverage b/tests/coverage/bad_counter_ids.coverage index d69ebf160ea..5474ec2f785 100644 --- a/tests/coverage/bad_counter_ids.coverage +++ b/tests/coverage/bad_counter_ids.coverage @@ -9,7 +9,7 @@ LL| |// a too-large counter ID and silently discard the entire function from its LL| |// coverage reports. LL| | - LL| 8|#[derive(Debug, PartialEq, Eq)] + LL| |#[derive(Debug, PartialEq, Eq)] LL| |struct Foo(u32); LL| | LL| 1|fn eq_good() { diff --git a/tests/coverage/issue-83601.cov-map b/tests/coverage/issue-83601.cov-map index f5db3a89750..f2447e3c92c 100644 --- a/tests/coverage/issue-83601.cov-map +++ b/tests/coverage/issue-83601.cov-map @@ -1,19 +1,3 @@ -Function name: <issue_83601::Foo as core::cmp::PartialEq>::eq -Raw bytes (9): 0x[01, 01, 00, 01, 01, 03, 11, 00, 1a] -Number of files: 1 -- file 0 => global file 1 -Number of expressions: 0 -Number of file 0 mappings: 1 -- Code(Counter(0)) at (prev + 3, 17) to (start + 0, 26) - -Function name: <issue_83601::Foo as core::fmt::Debug>::fmt -Raw bytes (9): 0x[01, 01, 00, 01, 01, 03, 0a, 00, 0f] -Number of files: 1 -- file 0 => global file 1 -Number of expressions: 0 -Number of file 0 mappings: 1 -- Code(Counter(0)) at (prev + 3, 10) to (start + 0, 15) - Function name: issue_83601::main Raw bytes (21): 0x[01, 01, 01, 05, 09, 03, 01, 06, 01, 02, 1c, 05, 03, 09, 01, 1c, 02, 02, 05, 03, 02] Number of files: 1 diff --git a/tests/coverage/issue-83601.coverage b/tests/coverage/issue-83601.coverage index 7995332cad3..e050106e6f0 100644 --- a/tests/coverage/issue-83601.coverage +++ b/tests/coverage/issue-83601.coverage @@ -1,7 +1,6 @@ LL| |// Shows that rust-lang/rust/83601 is resolved LL| | - LL| 3|#[derive(Debug, PartialEq, Eq)] - ^2 + LL| |#[derive(Debug, PartialEq, Eq)] LL| |struct Foo(u32); LL| | LL| 1|fn main() { diff --git a/tests/coverage/issue-84561.cov-map b/tests/coverage/issue-84561.cov-map index 6d577b3d485..88436964af0 100644 --- a/tests/coverage/issue-84561.cov-map +++ b/tests/coverage/issue-84561.cov-map @@ -1,11 +1,3 @@ -Function name: <issue_84561::Foo as core::cmp::PartialEq>::eq -Raw bytes (9): 0x[01, 01, 00, 01, 01, 04, 0a, 00, 13] -Number of files: 1 -- file 0 => global file 1 -Number of expressions: 0 -Number of file 0 mappings: 1 -- Code(Counter(0)) at (prev + 4, 10) to (start + 0, 19) - Function name: <issue_84561::Foo as core::fmt::Debug>::fmt Raw bytes (29): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 8a, 01, 05, 01, 25, 05, 01, 25, 00, 26, 02, 01, 09, 00, 0f, 07, 01, 05, 00, 06] Number of files: 1 diff --git a/tests/coverage/issue-84561.coverage b/tests/coverage/issue-84561.coverage index 9080d95eff5..90a2d069d39 100644 --- a/tests/coverage/issue-84561.coverage +++ b/tests/coverage/issue-84561.coverage @@ -1,7 +1,7 @@ LL| |// This demonstrated Issue #84561: function-like macros produce unintuitive coverage results. LL| | LL| |// failure-status: 101 - LL| 21|#[derive(PartialEq, Eq)] + LL| |#[derive(PartialEq, Eq)] LL| |struct Foo(u32); LL| | LL| |#[rustfmt::skip] diff --git a/tests/coverage/partial_eq.cov-map b/tests/coverage/partial_eq.cov-map index 5e4aae0260b..80670fbfa5a 100644 --- a/tests/coverage/partial_eq.cov-map +++ b/tests/coverage/partial_eq.cov-map @@ -1,51 +1,3 @@ -Function name: <partial_eq::Version as core::clone::Clone>::clone (unused) -Raw bytes (9): 0x[01, 01, 00, 01, 00, 04, 0a, 00, 0f] -Number of files: 1 -- file 0 => global file 1 -Number of expressions: 0 -Number of file 0 mappings: 1 -- Code(Zero) at (prev + 4, 10) to (start + 0, 15) - -Function name: <partial_eq::Version as core::cmp::Ord>::cmp (unused) -Raw bytes (14): 0x[01, 01, 00, 02, 00, 04, 33, 00, 34, 00, 00, 35, 00, 36] -Number of files: 1 -- file 0 => global file 1 -Number of expressions: 0 -Number of file 0 mappings: 2 -- Code(Zero) at (prev + 4, 51) to (start + 0, 52) -- Code(Zero) at (prev + 0, 53) to (start + 0, 54) - -Function name: <partial_eq::Version as core::cmp::PartialEq>::eq (unused) -Raw bytes (14): 0x[01, 01, 00, 02, 00, 04, 18, 00, 19, 00, 00, 20, 00, 21] -Number of files: 1 -- file 0 => global file 1 -Number of expressions: 0 -Number of file 0 mappings: 2 -- Code(Zero) at (prev + 4, 24) to (start + 0, 25) -- Code(Zero) at (prev + 0, 32) to (start + 0, 33) - -Function name: <partial_eq::Version as core::cmp::PartialOrd>::partial_cmp -Raw bytes (22): 0x[01, 01, 04, 07, 0b, 00, 09, 0f, 15, 00, 11, 02, 01, 04, 27, 00, 28, 03, 00, 30, 00, 31] -Number of files: 1 -- file 0 => global file 1 -Number of expressions: 4 -- expression 0 operands: lhs = Expression(1, Add), rhs = Expression(2, Add) -- expression 1 operands: lhs = Zero, rhs = Counter(2) -- expression 2 operands: lhs = Expression(3, Add), rhs = Counter(5) -- expression 3 operands: lhs = Zero, rhs = Counter(4) -Number of file 0 mappings: 2 -- Code(Counter(0)) at (prev + 4, 39) to (start + 0, 40) -- Code(Expression(0, Add)) at (prev + 0, 48) to (start + 0, 49) - = ((Zero + c2) + ((Zero + c4) + c5)) - -Function name: <partial_eq::Version as core::fmt::Debug>::fmt -Raw bytes (9): 0x[01, 01, 00, 01, 01, 04, 11, 00, 16] -Number of files: 1 -- file 0 => global file 1 -Number of expressions: 0 -Number of file 0 mappings: 1 -- Code(Counter(0)) at (prev + 4, 17) to (start + 0, 22) - Function name: <partial_eq::Version>::new Raw bytes (9): 0x[01, 01, 00, 01, 01, 0c, 05, 06, 06] Number of files: 1 diff --git a/tests/coverage/partial_eq.coverage b/tests/coverage/partial_eq.coverage index 6e3696386a7..9de1c933570 100644 --- a/tests/coverage/partial_eq.coverage +++ b/tests/coverage/partial_eq.coverage @@ -1,8 +1,7 @@ LL| |// This test confirms an earlier problem was resolved, supporting the MIR graph generated by the LL| |// structure of this test. LL| | - LL| 2|#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] - ^0 ^0 ^0 ^1 ^1 ^0^0 + LL| |#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] LL| |pub struct Version { LL| | major: usize, LL| | minor: usize, diff --git a/tests/rustdoc/doc-cfg-target-feature.rs b/tests/rustdoc/doc-cfg-target-feature.rs index f1b000dc823..98d230c978b 100644 --- a/tests/rustdoc/doc-cfg-target-feature.rs +++ b/tests/rustdoc/doc-cfg-target-feature.rs @@ -1,7 +1,6 @@ // only-x86_64 // compile-flags:--test // should-fail -// no-system-llvm // #49723: rustdoc didn't add target features when extracting or running doctests diff --git a/tests/ui/array-slice-vec/repeat_empty_ok.stderr b/tests/ui/array-slice-vec/repeat_empty_ok.stderr index bc3a68c905d..c272d47d961 100644 --- a/tests/ui/array-slice-vec/repeat_empty_ok.stderr +++ b/tests/ui/array-slice-vec/repeat_empty_ok.stderr @@ -6,7 +6,7 @@ LL | let headers = [Header{value: &[]}; 128]; | = note: the `Copy` trait is required because this value will be copied for each element of the array = help: consider using `core::array::from_fn` to initialize the array - = help: see https://doc.rust-lang.org/stable/std/array/fn.from_fn.html# for more information + = help: see https://doc.rust-lang.org/stable/std/array/fn.from_fn.html for more information help: consider annotating `Header<'_>` with `#[derive(Copy)]` | LL + #[derive(Copy)] @@ -21,7 +21,7 @@ LL | let headers = [Header{value: &[0]}; 128]; | = note: the `Copy` trait is required because this value will be copied for each element of the array = help: consider using `core::array::from_fn` to initialize the array - = help: see https://doc.rust-lang.org/stable/std/array/fn.from_fn.html# for more information + = help: see https://doc.rust-lang.org/stable/std/array/fn.from_fn.html for more information help: consider annotating `Header<'_>` with `#[derive(Copy)]` | LL + #[derive(Copy)] diff --git a/tests/ui/associated-consts/issue-105330.rs b/tests/ui/associated-consts/issue-105330.rs index 6c6dae864f3..285e89cce49 100644 --- a/tests/ui/associated-consts/issue-105330.rs +++ b/tests/ui/associated-consts/issue-105330.rs @@ -14,6 +14,5 @@ fn foo<A: TraitWAssocConst<A=32>>() { //~ ERROR E0658 fn main<A: TraitWAssocConst<A=32>>() { //~^ ERROR E0658 - //~| ERROR E0131 foo::<Demo>(); } diff --git a/tests/ui/associated-consts/issue-105330.stderr b/tests/ui/associated-consts/issue-105330.stderr index b4c021d0f4f..452367bed12 100644 --- a/tests/ui/associated-consts/issue-105330.stderr +++ b/tests/ui/associated-consts/issue-105330.stderr @@ -43,13 +43,7 @@ LL | impl TraitWAssocConst for impl Demo { | = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0131]: `main` function is not allowed to have generic parameters - --> $DIR/issue-105330.rs:15:8 - | -LL | fn main<A: TraitWAssocConst<A=32>>() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `main` cannot have generic parameters - -error: aborting due to 6 previous errors +error: aborting due to 5 previous errors -Some errors have detailed explanations: E0131, E0404, E0562, E0658. -For more information about an error, try `rustc --explain E0131`. +Some errors have detailed explanations: E0404, E0562, E0658. +For more information about an error, try `rustc --explain E0404`. diff --git a/tests/ui/associated-types/impl-wf-cycle-1.stderr b/tests/ui/associated-types/impl-wf-cycle-1.stderr index 5fa36733f66..d642f9f0e36 100644 --- a/tests/ui/associated-types/impl-wf-cycle-1.stderr +++ b/tests/ui/associated-types/impl-wf-cycle-1.stderr @@ -7,6 +7,9 @@ LL | | where LL | | Self::A: Baz, LL | | Self::B: Fiz, | |_________________^ +LL | { +LL | type A = (); + | ------ associated type `<(T,) as Grault>::A` is specified here | note: required for `(T,)` to implement `Grault` --> $DIR/impl-wf-cycle-1.rs:15:17 @@ -18,6 +21,10 @@ LL | Self::A: Baz, | --- unsatisfied trait bound introduced here = note: 1 redundant requirement hidden = note: required for `(T,)` to implement `Grault` +help: associated type for the current `impl` cannot be restricted in `where` clauses, remove this bound + | +LL - Self::A: Baz, + | error: aborting due to 1 previous error diff --git a/tests/ui/associated-types/impl-wf-cycle-2.stderr b/tests/ui/associated-types/impl-wf-cycle-2.stderr index 17a96bbd934..9454d1d6697 100644 --- a/tests/ui/associated-types/impl-wf-cycle-2.stderr +++ b/tests/ui/associated-types/impl-wf-cycle-2.stderr @@ -6,6 +6,9 @@ LL | | LL | | where LL | | Self::A: Copy, | |__________________^ +LL | { +LL | type A = (); + | ------ associated type `<(T,) as Grault>::A` is specified here | note: required for `(T,)` to implement `Grault` --> $DIR/impl-wf-cycle-2.rs:7:17 @@ -15,6 +18,11 @@ LL | impl<T: Grault> Grault for (T,) ... LL | Self::A: Copy, | ---- unsatisfied trait bound introduced here +help: associated type for the current `impl` cannot be restricted in `where` clauses, remove this bound + | +LL - where +LL - Self::A: Copy, + | error: aborting due to 1 previous error diff --git a/tests/ui/associated-types/impl-wf-cycle-3.rs b/tests/ui/associated-types/impl-wf-cycle-3.rs new file mode 100644 index 00000000000..044033c5e8a --- /dev/null +++ b/tests/ui/associated-types/impl-wf-cycle-3.rs @@ -0,0 +1,13 @@ +trait A<T> {} + +trait B { + type Type; +} + +impl<T> B for T //~ ERROR overflow evaluating the requirement +where + T: A<Self::Type>, +{ + type Type = bool; +} +fn main() {} diff --git a/tests/ui/associated-types/impl-wf-cycle-3.stderr b/tests/ui/associated-types/impl-wf-cycle-3.stderr new file mode 100644 index 00000000000..d3ca06f890b --- /dev/null +++ b/tests/ui/associated-types/impl-wf-cycle-3.stderr @@ -0,0 +1,27 @@ +error[E0275]: overflow evaluating the requirement `<T as B>::Type == <T as B>::Type` + --> $DIR/impl-wf-cycle-3.rs:7:1 + | +LL | / impl<T> B for T +LL | | where +LL | | T: A<Self::Type>, + | |_____________________^ +LL | { +LL | type Type = bool; + | --------- associated type `<T as B>::Type` is specified here + | +note: required for `T` to implement `B` + --> $DIR/impl-wf-cycle-3.rs:7:9 + | +LL | impl<T> B for T + | ^ ^ +LL | where +LL | T: A<Self::Type>, + | ------------- unsatisfied trait bound introduced here +help: replace the associated type with the type specified in this `impl` + | +LL | T: A<bool>, + | ~~~~ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/associated-types/impl-wf-cycle-4.rs b/tests/ui/associated-types/impl-wf-cycle-4.rs new file mode 100644 index 00000000000..bfa8adc71a1 --- /dev/null +++ b/tests/ui/associated-types/impl-wf-cycle-4.rs @@ -0,0 +1,15 @@ +trait Filter { + type ToMatch; +} + +impl<T> Filter for T //~ ERROR overflow evaluating the requirement +where + T: Fn(Self::ToMatch), +{ +} + +struct JustFilter<F: Filter> { + filter: F, +} + +fn main() {} diff --git a/tests/ui/associated-types/impl-wf-cycle-4.stderr b/tests/ui/associated-types/impl-wf-cycle-4.stderr new file mode 100644 index 00000000000..cdbac267d34 --- /dev/null +++ b/tests/ui/associated-types/impl-wf-cycle-4.stderr @@ -0,0 +1,25 @@ +error[E0275]: overflow evaluating the requirement `<T as Filter>::ToMatch == <T as Filter>::ToMatch` + --> $DIR/impl-wf-cycle-4.rs:5:1 + | +LL | / impl<T> Filter for T +LL | | where +LL | | T: Fn(Self::ToMatch), + | |_________________________^ + | +note: required for `T` to implement `Filter` + --> $DIR/impl-wf-cycle-4.rs:5:9 + | +LL | impl<T> Filter for T + | ^^^^^^ ^ +LL | where +LL | T: Fn(Self::ToMatch), + | ----------------- unsatisfied trait bound introduced here +note: associated types for the current `impl` cannot be restricted in `where` clauses + --> $DIR/impl-wf-cycle-4.rs:7:11 + | +LL | T: Fn(Self::ToMatch), + | ^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/associated-types/impl-wf-cycle-5.fixed b/tests/ui/associated-types/impl-wf-cycle-5.fixed new file mode 100644 index 00000000000..bff6ca90975 --- /dev/null +++ b/tests/ui/associated-types/impl-wf-cycle-5.fixed @@ -0,0 +1,31 @@ +// run-rustfix + +trait Baz {} +impl Baz for () {} +impl<T> Baz for (T,) {} + +trait Fiz {} +impl Fiz for bool {} + +trait Grault { + type A; + type B; +} + +impl Grault for () { + type A = (); + type B = bool; +} + +impl<T> Grault for (T,) +//~^ ERROR overflow evaluating the requirement `<(T,) as Grault>::A == _` +where + T: Grault, +{ + type A = (); + type B = bool; +} + +fn main() { + let _: <((),) as Grault>::A = (); +} diff --git a/tests/ui/associated-types/impl-wf-cycle-5.rs b/tests/ui/associated-types/impl-wf-cycle-5.rs new file mode 100644 index 00000000000..a822e1fb008 --- /dev/null +++ b/tests/ui/associated-types/impl-wf-cycle-5.rs @@ -0,0 +1,32 @@ +// run-rustfix + +trait Baz {} +impl Baz for () {} +impl<T> Baz for (T,) {} + +trait Fiz {} +impl Fiz for bool {} + +trait Grault { + type A; + type B; +} + +impl Grault for () { + type A = (); + type B = bool; +} + +impl<T> Grault for (T,) +//~^ ERROR overflow evaluating the requirement `<(T,) as Grault>::A == _` +where + T: Grault, + Self::A: Baz, +{ + type A = (); + type B = bool; +} + +fn main() { + let _: <((),) as Grault>::A = (); +} diff --git a/tests/ui/associated-types/impl-wf-cycle-5.stderr b/tests/ui/associated-types/impl-wf-cycle-5.stderr new file mode 100644 index 00000000000..284a50bb9a3 --- /dev/null +++ b/tests/ui/associated-types/impl-wf-cycle-5.stderr @@ -0,0 +1,31 @@ +error[E0275]: overflow evaluating the requirement `<(T,) as Grault>::A == _` + --> $DIR/impl-wf-cycle-5.rs:20:1 + | +LL | / impl<T> Grault for (T,) +LL | | +LL | | where +LL | | T: Grault, +LL | | Self::A: Baz, + | |_________________^ +LL | { +LL | type A = (); + | ------ associated type `<(T,) as Grault>::A` is specified here + | +note: required for `(T,)` to implement `Grault` + --> $DIR/impl-wf-cycle-5.rs:20:9 + | +LL | impl<T> Grault for (T,) + | ^^^^^^ ^^^^ +... +LL | Self::A: Baz, + | --- unsatisfied trait bound introduced here +help: associated type for the current `impl` cannot be restricted in `where` clauses, remove this bound + | +LL - T: Grault, +LL - Self::A: Baz, +LL + T: Grault, + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/associated-types/impl-wf-cycle-6.fixed b/tests/ui/associated-types/impl-wf-cycle-6.fixed new file mode 100644 index 00000000000..73ed64f7ce3 --- /dev/null +++ b/tests/ui/associated-types/impl-wf-cycle-6.fixed @@ -0,0 +1,30 @@ +// run-rustfix + +trait Baz {} +impl Baz for () {} +impl<T> Baz for (T,) {} + +trait Fiz {} +impl Fiz for bool {} + +trait Grault { + type A; + type B; +} + +impl Grault for () { + type A = (); + type B = bool; +} + +impl<T: Grault> Grault for (T,) +//~^ ERROR overflow evaluating the requirement `<(T,) as Grault>::A == _` + +{ + type A = (); + type B = bool; +} + +fn main() { + let _: <((),) as Grault>::A = (); +} diff --git a/tests/ui/associated-types/impl-wf-cycle-6.rs b/tests/ui/associated-types/impl-wf-cycle-6.rs new file mode 100644 index 00000000000..20d635cac5d --- /dev/null +++ b/tests/ui/associated-types/impl-wf-cycle-6.rs @@ -0,0 +1,31 @@ +// run-rustfix + +trait Baz {} +impl Baz for () {} +impl<T> Baz for (T,) {} + +trait Fiz {} +impl Fiz for bool {} + +trait Grault { + type A; + type B; +} + +impl Grault for () { + type A = (); + type B = bool; +} + +impl<T: Grault> Grault for (T,) +//~^ ERROR overflow evaluating the requirement `<(T,) as Grault>::A == _` +where + Self::A: Baz, +{ + type A = (); + type B = bool; +} + +fn main() { + let _: <((),) as Grault>::A = (); +} diff --git a/tests/ui/associated-types/impl-wf-cycle-6.stderr b/tests/ui/associated-types/impl-wf-cycle-6.stderr new file mode 100644 index 00000000000..c9b5d8060be --- /dev/null +++ b/tests/ui/associated-types/impl-wf-cycle-6.stderr @@ -0,0 +1,29 @@ +error[E0275]: overflow evaluating the requirement `<(T,) as Grault>::A == _` + --> $DIR/impl-wf-cycle-6.rs:20:1 + | +LL | / impl<T: Grault> Grault for (T,) +LL | | +LL | | where +LL | | Self::A: Baz, + | |_________________^ +LL | { +LL | type A = (); + | ------ associated type `<(T,) as Grault>::A` is specified here + | +note: required for `(T,)` to implement `Grault` + --> $DIR/impl-wf-cycle-6.rs:20:17 + | +LL | impl<T: Grault> Grault for (T,) + | ^^^^^^ ^^^^ +... +LL | Self::A: Baz, + | --- unsatisfied trait bound introduced here +help: associated type for the current `impl` cannot be restricted in `where` clauses, remove this bound + | +LL - where +LL - Self::A: Baz, + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/associated-types/issue-38821.rs b/tests/ui/associated-types/issue-38821.rs index 6753860e9ff..34c35d7acf1 100644 --- a/tests/ui/associated-types/issue-38821.rs +++ b/tests/ui/associated-types/issue-38821.rs @@ -22,7 +22,22 @@ pub trait Column: Expression {} #[derive(Debug, Copy, Clone)] //~^ ERROR the trait bound `<Col as Expression>::SqlType: NotNull` is not satisfied +//~| ERROR the trait bound `<Col as Expression>::SqlType: NotNull` is not satisfied +//~| ERROR the trait bound `<Col as Expression>::SqlType: NotNull` is not satisfied +//~| ERROR the trait bound `<Col as Expression>::SqlType: NotNull` is not satisfied +//~| ERROR the trait bound `<Col as Expression>::SqlType: NotNull` is not satisfied +//~| ERROR the trait bound `<Col as Expression>::SqlType: NotNull` is not satisfied +//~| ERROR the trait bound `<Col as Expression>::SqlType: NotNull` is not satisfied +//~| ERROR the trait bound `<Col as Expression>::SqlType: NotNull` is not satisfied +//~| ERROR the trait bound `<Col as Expression>::SqlType: NotNull` is not satisfied +//~| ERROR the trait bound `<Col as Expression>::SqlType: NotNull` is not satisfied +//~| ERROR the trait bound `<Col as Expression>::SqlType: NotNull` is not satisfied +//~| ERROR the trait bound `<Col as Expression>::SqlType: NotNull` is not satisfied +//~| ERROR the trait bound `<Col as Expression>::SqlType: NotNull` is not satisfied +//~| ERROR the trait bound `<Col as Expression>::SqlType: NotNull` is not satisfied pub enum ColumnInsertValue<Col, Expr> where +//~^ ERROR the trait bound `<Col as Expression>::SqlType: NotNull` is not satisfied +//~| ERROR the trait bound `<Col as Expression>::SqlType: NotNull` is not satisfied Col: Column, Expr: Expression<SqlType=<Col::SqlType as IntoNullable>::Nullable>, { diff --git a/tests/ui/associated-types/issue-38821.stderr b/tests/ui/associated-types/issue-38821.stderr index 8b628f5ae6d..0e34f541ea3 100644 --- a/tests/ui/associated-types/issue-38821.stderr +++ b/tests/ui/associated-types/issue-38821.stderr @@ -17,6 +17,272 @@ help: consider further restricting the associated type LL | Expr: Expression<SqlType=<Col::SqlType as IntoNullable>::Nullable>, <Col as Expression>::SqlType: NotNull, | +++++++++++++++++++++++++++++++++++++++ -error: aborting due to 1 previous error +error[E0277]: the trait bound `<Col as Expression>::SqlType: NotNull` is not satisfied + --> $DIR/issue-38821.rs:38:1 + | +LL | pub enum ColumnInsertValue<Col, Expr> where + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `NotNull` is not implemented for `<Col as Expression>::SqlType` + | +note: required for `<Col as Expression>::SqlType` to implement `IntoNullable` + --> $DIR/issue-38821.rs:9:18 + | +LL | impl<T: NotNull> IntoNullable for T { + | ------- ^^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here +help: consider extending the `where` clause, but there might be an alternative better way to express this requirement + | +LL | Expr: Expression<SqlType=<Col::SqlType as IntoNullable>::Nullable>, <Col as Expression>::SqlType: NotNull + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error[E0277]: the trait bound `<Col as Expression>::SqlType: NotNull` is not satisfied + --> $DIR/issue-38821.rs:38:1 + | +LL | / pub enum ColumnInsertValue<Col, Expr> where +LL | | +LL | | +LL | | Col: Column, +... | +LL | | Default(Col), +LL | | } + | |_^ the trait `NotNull` is not implemented for `<Col as Expression>::SqlType` + | +note: required for `<Col as Expression>::SqlType` to implement `IntoNullable` + --> $DIR/issue-38821.rs:9:18 + | +LL | impl<T: NotNull> IntoNullable for T { + | ------- ^^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here +help: consider extending the `where` clause, but there might be an alternative better way to express this requirement + | +LL | Expr: Expression<SqlType=<Col::SqlType as IntoNullable>::Nullable>, <Col as Expression>::SqlType: NotNull + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error[E0277]: the trait bound `<Col as Expression>::SqlType: NotNull` is not satisfied + --> $DIR/issue-38821.rs:23:10 + | +LL | #[derive(Debug, Copy, Clone)] + | ^^^^^ the trait `NotNull` is not implemented for `<Col as Expression>::SqlType` + | +note: required for `<Col as Expression>::SqlType` to implement `IntoNullable` + --> $DIR/issue-38821.rs:9:18 + | +LL | impl<T: NotNull> IntoNullable for T { + | ------- ^^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider further restricting the associated type + | +LL | Expr: Expression<SqlType=<Col::SqlType as IntoNullable>::Nullable>, <Col as Expression>::SqlType: NotNull, + | +++++++++++++++++++++++++++++++++++++++ + +error[E0277]: the trait bound `<Col as Expression>::SqlType: NotNull` is not satisfied + --> $DIR/issue-38821.rs:23:10 + | +LL | #[derive(Debug, Copy, Clone)] + | ^^^^^ the trait `NotNull` is not implemented for `<Col as Expression>::SqlType` + | +note: required for `<Col as Expression>::SqlType` to implement `IntoNullable` + --> $DIR/issue-38821.rs:9:18 + | +LL | impl<T: NotNull> IntoNullable for T { + | ------- ^^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider further restricting the associated type + | +LL | Expr: Expression<SqlType=<Col::SqlType as IntoNullable>::Nullable>, <Col as Expression>::SqlType: NotNull, + | +++++++++++++++++++++++++++++++++++++++ + +error[E0277]: the trait bound `<Col as Expression>::SqlType: NotNull` is not satisfied + --> $DIR/issue-38821.rs:23:10 + | +LL | #[derive(Debug, Copy, Clone)] + | ^^^^^ the trait `NotNull` is not implemented for `<Col as Expression>::SqlType` + | +note: required for `<Col as Expression>::SqlType` to implement `IntoNullable` + --> $DIR/issue-38821.rs:9:18 + | +LL | impl<T: NotNull> IntoNullable for T { + | ------- ^^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `<Col as Expression>::SqlType: NotNull` is not satisfied + --> $DIR/issue-38821.rs:23:10 + | +LL | #[derive(Debug, Copy, Clone)] + | ^^^^^ the trait `NotNull` is not implemented for `<Col as Expression>::SqlType` + | +note: required for `<Col as Expression>::SqlType` to implement `IntoNullable` + --> $DIR/issue-38821.rs:9:18 + | +LL | impl<T: NotNull> IntoNullable for T { + | ------- ^^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `<Col as Expression>::SqlType: NotNull` is not satisfied + --> $DIR/issue-38821.rs:23:17 + | +LL | #[derive(Debug, Copy, Clone)] + | ^^^^ the trait `NotNull` is not implemented for `<Col as Expression>::SqlType` + | +note: required for `<Col as Expression>::SqlType` to implement `IntoNullable` + --> $DIR/issue-38821.rs:9:18 + | +LL | impl<T: NotNull> IntoNullable for T { + | ------- ^^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider further restricting the associated type + | +LL | Expr: Expression<SqlType=<Col::SqlType as IntoNullable>::Nullable>, <Col as Expression>::SqlType: NotNull, + | +++++++++++++++++++++++++++++++++++++++ + +error[E0277]: the trait bound `<Col as Expression>::SqlType: NotNull` is not satisfied + --> $DIR/issue-38821.rs:23:23 + | +LL | #[derive(Debug, Copy, Clone)] + | ^^^^^ the trait `NotNull` is not implemented for `<Col as Expression>::SqlType` + | +note: required for `<Col as Expression>::SqlType` to implement `IntoNullable` + --> $DIR/issue-38821.rs:9:18 + | +LL | impl<T: NotNull> IntoNullable for T { + | ------- ^^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider further restricting the associated type + | +LL | Expr: Expression<SqlType=<Col::SqlType as IntoNullable>::Nullable>, <Col as Expression>::SqlType: NotNull, + | +++++++++++++++++++++++++++++++++++++++ + +error[E0277]: the trait bound `<Col as Expression>::SqlType: NotNull` is not satisfied + --> $DIR/issue-38821.rs:23:23 + | +LL | #[derive(Debug, Copy, Clone)] + | ^^^^^ the trait `NotNull` is not implemented for `<Col as Expression>::SqlType` + | +note: required for `<Col as Expression>::SqlType` to implement `IntoNullable` + --> $DIR/issue-38821.rs:9:18 + | +LL | impl<T: NotNull> IntoNullable for T { + | ------- ^^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider further restricting the associated type + | +LL | Expr: Expression<SqlType=<Col::SqlType as IntoNullable>::Nullable>, <Col as Expression>::SqlType: NotNull, + | +++++++++++++++++++++++++++++++++++++++ + +error[E0277]: the trait bound `<Col as Expression>::SqlType: NotNull` is not satisfied + --> $DIR/issue-38821.rs:23:23 + | +LL | #[derive(Debug, Copy, Clone)] + | ^^^^^ the trait `NotNull` is not implemented for `<Col as Expression>::SqlType` + | +note: required for `<Col as Expression>::SqlType` to implement `IntoNullable` + --> $DIR/issue-38821.rs:9:18 + | +LL | impl<T: NotNull> IntoNullable for T { + | ------- ^^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `<Col as Expression>::SqlType: NotNull` is not satisfied + --> $DIR/issue-38821.rs:23:23 + | +LL | #[derive(Debug, Copy, Clone)] + | ^^^^^ the trait `NotNull` is not implemented for `<Col as Expression>::SqlType` + | +note: required for `<Col as Expression>::SqlType` to implement `IntoNullable` + --> $DIR/issue-38821.rs:9:18 + | +LL | impl<T: NotNull> IntoNullable for T { + | ------- ^^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `<Col as Expression>::SqlType: NotNull` is not satisfied + --> $DIR/issue-38821.rs:23:10 + | +LL | #[derive(Debug, Copy, Clone)] + | ^^^^^ the trait `NotNull` is not implemented for `<Col as Expression>::SqlType` + | +note: required for `<Col as Expression>::SqlType` to implement `IntoNullable` + --> $DIR/issue-38821.rs:9:18 + | +LL | impl<T: NotNull> IntoNullable for T { + | ------- ^^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `<Col as Expression>::SqlType: NotNull` is not satisfied + --> $DIR/issue-38821.rs:23:10 + | +LL | #[derive(Debug, Copy, Clone)] + | ^^^^^ the trait `NotNull` is not implemented for `<Col as Expression>::SqlType` + | +note: required for `<Col as Expression>::SqlType` to implement `IntoNullable` + --> $DIR/issue-38821.rs:9:18 + | +LL | impl<T: NotNull> IntoNullable for T { + | ------- ^^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `<Col as Expression>::SqlType: NotNull` is not satisfied + --> $DIR/issue-38821.rs:23:23 + | +LL | #[derive(Debug, Copy, Clone)] + | ^^^^^ the trait `NotNull` is not implemented for `<Col as Expression>::SqlType` + | +note: required for `<Col as Expression>::SqlType` to implement `IntoNullable` + --> $DIR/issue-38821.rs:9:18 + | +LL | impl<T: NotNull> IntoNullable for T { + | ------- ^^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `<Col as Expression>::SqlType: NotNull` is not satisfied + --> $DIR/issue-38821.rs:23:23 + | +LL | #[derive(Debug, Copy, Clone)] + | ^^^^^ the trait `NotNull` is not implemented for `<Col as Expression>::SqlType` + | +note: required for `<Col as Expression>::SqlType` to implement `IntoNullable` + --> $DIR/issue-38821.rs:9:18 + | +LL | impl<T: NotNull> IntoNullable for T { + | ------- ^^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 16 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/async-await/async-fn/simple.rs b/tests/ui/async-await/async-fn/simple.rs new file mode 100644 index 00000000000..99a5d56a309 --- /dev/null +++ b/tests/ui/async-await/async-fn/simple.rs @@ -0,0 +1,16 @@ +// edition: 2021 +// check-pass + +#![feature(async_fn_traits)] + +use std::ops::AsyncFn; + +async fn foo() {} + +async fn call_asyncly(f: impl AsyncFn(i32) -> i32) -> i32 { + f(1).await +} + +fn main() { + let fut = call_asyncly(|x| async move { x + 1 }); +} diff --git a/tests/ui/check-cfg/well-known-values.stderr b/tests/ui/check-cfg/well-known-values.stderr index 814d4736197..d7d538c0b9e 100644 --- a/tests/ui/check-cfg/well-known-values.stderr +++ b/tests/ui/check-cfg/well-known-values.stderr @@ -125,7 +125,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_env = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_env` are: ``, `eabihf`, `gnu`, `gnueabihf`, `msvc`, `musl`, `newlib`, `nto70`, `nto71`, `ohos`, `psx`, `relibc`, `sgx`, `uclibc` + = note: expected values for `target_env` are: ``, `eabihf`, `gnu`, `gnueabihf`, `msvc`, `musl`, `newlib`, `nto70`, `nto71`, `ohos`, `preview2`, `psx`, `relibc`, `sgx`, `uclibc` = note: see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -134,7 +134,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_family = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_family` are: `unix`, `wasm`, `windows` + = note: expected values for `target_family` are: `unix`, `wasi`, `wasm`, `windows` = note: see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` diff --git a/tests/ui/coercion/issue-53475.rs b/tests/ui/coercion/issue-53475.rs index 3770c024fb9..f389169f954 100644 --- a/tests/ui/coercion/issue-53475.rs +++ b/tests/ui/coercion/issue-53475.rs @@ -3,7 +3,7 @@ use std::any::Any; use std::ops::CoerceUnsized; -struct Foo<T> { +struct Foo<T: ?Sized> { data: Box<T>, } diff --git a/tests/ui/const-generics/bad-generic-in-copy-impl.rs b/tests/ui/const-generics/bad-generic-in-copy-impl.rs index b5663464cf4..092a7af236b 100644 --- a/tests/ui/const-generics/bad-generic-in-copy-impl.rs +++ b/tests/ui/const-generics/bad-generic-in-copy-impl.rs @@ -2,6 +2,7 @@ pub struct Foo { x: [u8; SIZE], //~^ ERROR mismatched types + //~| ERROR mismatched types } const SIZE: u32 = 1; diff --git a/tests/ui/const-generics/bad-generic-in-copy-impl.stderr b/tests/ui/const-generics/bad-generic-in-copy-impl.stderr index d60d8ec0e90..fbd546d42fb 100644 --- a/tests/ui/const-generics/bad-generic-in-copy-impl.stderr +++ b/tests/ui/const-generics/bad-generic-in-copy-impl.stderr @@ -4,6 +4,14 @@ error[E0308]: mismatched types LL | x: [u8; SIZE], | ^^^^ expected `usize`, found `u32` -error: aborting due to 1 previous error +error[E0308]: mismatched types + --> $DIR/bad-generic-in-copy-impl.rs:3:13 + | +LL | x: [u8; SIZE], + | ^^^^ expected `usize`, found `u32` + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/const-generics/issues/issue-61336-2.stderr b/tests/ui/const-generics/issues/issue-61336-2.stderr index 9064c2d0b94..b0864689f74 100644 --- a/tests/ui/const-generics/issues/issue-61336-2.stderr +++ b/tests/ui/const-generics/issues/issue-61336-2.stderr @@ -6,7 +6,7 @@ LL | [x; { N }] | = note: the `Copy` trait is required because this value will be copied for each element of the array = help: consider using `core::array::from_fn` to initialize the array - = help: see https://doc.rust-lang.org/stable/std/array/fn.from_fn.html# for more information + = help: see https://doc.rust-lang.org/stable/std/array/fn.from_fn.html for more information help: consider restricting type parameter `T` | LL | fn g<T: std::marker::Copy, const N: usize>(x: T) -> [T; N] { diff --git a/tests/ui/const-generics/issues/issue-61336.stderr b/tests/ui/const-generics/issues/issue-61336.stderr index 9935d6c1689..111afbda343 100644 --- a/tests/ui/const-generics/issues/issue-61336.stderr +++ b/tests/ui/const-generics/issues/issue-61336.stderr @@ -6,7 +6,7 @@ LL | [x; N] | = note: the `Copy` trait is required because this value will be copied for each element of the array = help: consider using `core::array::from_fn` to initialize the array - = help: see https://doc.rust-lang.org/stable/std/array/fn.from_fn.html# for more information + = help: see https://doc.rust-lang.org/stable/std/array/fn.from_fn.html for more information help: consider restricting type parameter `T` | LL | fn g<T: std::marker::Copy, const N: usize>(x: T) -> [T; N] { diff --git a/tests/ui/consts/const-blocks/migrate-fail.stderr b/tests/ui/consts/const-blocks/migrate-fail.stderr index 95fece0ae8a..3c116026e58 100644 --- a/tests/ui/consts/const-blocks/migrate-fail.stderr +++ b/tests/ui/consts/const-blocks/migrate-fail.stderr @@ -7,7 +7,7 @@ LL | let arr: [Option<Bar>; 2] = [x; 2]; = note: required for `Option<Bar>` to implement `Copy` = note: the `Copy` trait is required because this value will be copied for each element of the array = help: consider using `core::array::from_fn` to initialize the array - = help: see https://doc.rust-lang.org/stable/std/array/fn.from_fn.html# for more information + = help: see https://doc.rust-lang.org/stable/std/array/fn.from_fn.html for more information help: consider annotating `Bar` with `#[derive(Copy)]` | LL + #[derive(Copy)] @@ -23,7 +23,7 @@ LL | let arr: [Option<Bar>; 2] = [x; 2]; = note: required for `Option<Bar>` to implement `Copy` = note: the `Copy` trait is required because this value will be copied for each element of the array = help: consider using `core::array::from_fn` to initialize the array - = help: see https://doc.rust-lang.org/stable/std/array/fn.from_fn.html# for more information + = help: see https://doc.rust-lang.org/stable/std/array/fn.from_fn.html for more information help: consider annotating `Bar` with `#[derive(Copy)]` | LL + #[derive(Copy)] diff --git a/tests/ui/consts/const-blocks/nll-fail.stderr b/tests/ui/consts/const-blocks/nll-fail.stderr index ed1dc78f77d..ff2b62da668 100644 --- a/tests/ui/consts/const-blocks/nll-fail.stderr +++ b/tests/ui/consts/const-blocks/nll-fail.stderr @@ -7,7 +7,7 @@ LL | let arr: [Option<Bar>; 2] = [x; 2]; = note: required for `Option<Bar>` to implement `Copy` = note: the `Copy` trait is required because this value will be copied for each element of the array = help: consider using `core::array::from_fn` to initialize the array - = help: see https://doc.rust-lang.org/stable/std/array/fn.from_fn.html# for more information + = help: see https://doc.rust-lang.org/stable/std/array/fn.from_fn.html for more information help: consider annotating `Bar` with `#[derive(Copy)]` | LL + #[derive(Copy)] @@ -23,7 +23,7 @@ LL | let arr: [Option<Bar>; 2] = [x; 2]; = note: required for `Option<Bar>` to implement `Copy` = note: the `Copy` trait is required because this value will be copied for each element of the array = help: consider using `core::array::from_fn` to initialize the array - = help: see https://doc.rust-lang.org/stable/std/array/fn.from_fn.html# for more information + = help: see https://doc.rust-lang.org/stable/std/array/fn.from_fn.html for more information help: consider annotating `Bar` with `#[derive(Copy)]` | LL + #[derive(Copy)] diff --git a/tests/ui/consts/const-err-late.stderr b/tests/ui/consts/const-err-late.stderr index 35c3d000117..53badeafa35 100644 --- a/tests/ui/consts/const-err-late.stderr +++ b/tests/ui/consts/const-err-late.stderr @@ -30,6 +30,14 @@ LL | black_box((S::<i32>::FOO, S::<u32>::FOO)); | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +note: erroneous constant encountered + --> $DIR/const-err-late.rs:19:31 + | +LL | black_box((S::<i32>::FOO, S::<u32>::FOO)); + | ^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/is_val_statically_known.rs b/tests/ui/consts/is_val_statically_known.rs new file mode 100644 index 00000000000..b0565842eb4 --- /dev/null +++ b/tests/ui/consts/is_val_statically_known.rs @@ -0,0 +1,15 @@ +// run-pass + +#![feature(core_intrinsics)] +#![feature(is_val_statically_known)] + +use std::intrinsics::is_val_statically_known; + +const CONST_TEST: bool = unsafe { is_val_statically_known(0) }; + +fn main() { + if CONST_TEST { + unreachable!("currently expected to return false during const eval"); + // but note that this is not a guarantee! + } +} diff --git a/tests/ui/consts/issue-65348.rs b/tests/ui/consts/issue-65348.rs index 5eafa831d63..01bf2a3fa42 100644 --- a/tests/ui/consts/issue-65348.rs +++ b/tests/ui/consts/issue-65348.rs @@ -8,15 +8,16 @@ impl<T> Generic<T> { const ARRAY_FIELD: Generic<(i32, [T; 0])> = Generic((0, [])); } -pub const fn array<T>() -> &'static T { +pub const fn array<T>() -> &'static T { + #[allow(unconditional_panic)] &Generic::<T>::ARRAY[0] } -pub const fn newtype_array<T>() -> &'static T { +pub const fn newtype_array<T>() -> &'static T { &Generic::<T>::NEWTYPE_ARRAY.0[0] } -pub const fn array_field<T>() -> &'static T { +pub const fn array_field<T>() -> &'static T { &(Generic::<T>::ARRAY_FIELD.0).1[0] } diff --git a/tests/ui/did_you_mean/bad-assoc-ty.stderr b/tests/ui/did_you_mean/bad-assoc-ty.stderr index 3c474d19d1d..eed01267224 100644 --- a/tests/ui/did_you_mean/bad-assoc-ty.stderr +++ b/tests/ui/did_you_mean/bad-assoc-ty.stderr @@ -191,7 +191,14 @@ error[E0223]: ambiguous associated type --> $DIR/bad-assoc-ty.rs:33:10 | LL | type H = Fn(u8) -> (u8)::Output; - | ^^^^^^^^^^^^^^^^^^^^^^ help: use fully-qualified syntax: `<(dyn Fn(u8) -> u8 + 'static) as IntoFuture>::Output` + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: use fully-qualified syntax + | +LL | type H = <(dyn Fn(u8) -> u8 + 'static) as AsyncFnOnce>::Output; + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL | type H = <(dyn Fn(u8) -> u8 + 'static) as IntoFuture>::Output; + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error[E0223]: ambiguous associated type --> $DIR/bad-assoc-ty.rs:39:19 diff --git a/tests/ui/dst/dst-bad-assign-3.stderr b/tests/ui/dst/dst-bad-assign-3.stderr index 6dd3434fd21..007f6b898be 100644 --- a/tests/ui/dst/dst-bad-assign-3.stderr +++ b/tests/ui/dst/dst-bad-assign-3.stderr @@ -8,6 +8,7 @@ LL | f5.2 = Bar1 {f: 36}; | = note: expected trait object `dyn ToBar` found struct `Bar1` + = help: `Bar1` implements `ToBar` so you could box the found value and coerce it to the trait object `Box<dyn ToBar>`, you will have to change the expected type as well error[E0277]: the size for values of type `dyn ToBar` cannot be known at compilation time --> $DIR/dst-bad-assign-3.rs:33:5 diff --git a/tests/ui/dst/dst-bad-assign.stderr b/tests/ui/dst/dst-bad-assign.stderr index d8d1057876f..f935d27e96e 100644 --- a/tests/ui/dst/dst-bad-assign.stderr +++ b/tests/ui/dst/dst-bad-assign.stderr @@ -8,6 +8,7 @@ LL | f5.ptr = Bar1 {f: 36}; | = note: expected trait object `dyn ToBar` found struct `Bar1` + = help: `Bar1` implements `ToBar` so you could box the found value and coerce it to the trait object `Box<dyn ToBar>`, you will have to change the expected type as well error[E0277]: the size for values of type `dyn ToBar` cannot be known at compilation time --> $DIR/dst-bad-assign.rs:35:5 diff --git a/tests/ui/dyn-star/no-implicit-dyn-star.stderr b/tests/ui/dyn-star/no-implicit-dyn-star.stderr index 06c7bcc5748..bea334a8a69 100644 --- a/tests/ui/dyn-star/no-implicit-dyn-star.stderr +++ b/tests/ui/dyn-star/no-implicit-dyn-star.stderr @@ -8,6 +8,7 @@ LL | dyn_star_foreign::require_dyn_star_display(1usize); | = note: expected trait object `(dyn* std::fmt::Display + 'static)` found type `usize` + = help: `usize` implements `Display`, `#[feature(dyn_star)]` is likely not enabled; that feature it is currently incomplete note: function defined here --> $DIR/auxiliary/dyn-star-foreign.rs:6:8 | diff --git a/tests/ui/enum-discriminant/issue-72554.rs b/tests/ui/enum-discriminant/issue-72554.rs index 54f7e9ac592..1fe9a5f4faa 100644 --- a/tests/ui/enum-discriminant/issue-72554.rs +++ b/tests/ui/enum-discriminant/issue-72554.rs @@ -3,6 +3,7 @@ use std::collections::BTreeSet; #[derive(Hash)] pub enum ElemDerived { //~^ ERROR recursive type `ElemDerived` has infinite size + //~| ERROR cycle detected A(ElemDerived) } diff --git a/tests/ui/enum-discriminant/issue-72554.stderr b/tests/ui/enum-discriminant/issue-72554.stderr index 381f24d351e..648680c6031 100644 --- a/tests/ui/enum-discriminant/issue-72554.stderr +++ b/tests/ui/enum-discriminant/issue-72554.stderr @@ -3,7 +3,7 @@ error[E0072]: recursive type `ElemDerived` has infinite size | LL | pub enum ElemDerived { | ^^^^^^^^^^^^^^^^^^^^ -LL | +... LL | A(ElemDerived) | ----------- recursive without indirection | @@ -12,6 +12,21 @@ help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle LL | A(Box<ElemDerived>) | ++++ + -error: aborting due to 1 previous error +error[E0391]: cycle detected when computing drop-check constraints for `ElemDerived` + --> $DIR/issue-72554.rs:4:1 + | +LL | pub enum ElemDerived { + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: ...which immediately requires computing drop-check constraints for `ElemDerived` again +note: cycle used when computing drop-check constraints for `Elem` + --> $DIR/issue-72554.rs:11:1 + | +LL | pub enum Elem { + | ^^^^^^^^^^^^^ + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information + +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0072`. +Some errors have detailed explanations: E0072, E0391. +For more information about an error, try `rustc --explain E0072`. diff --git a/tests/ui/for-loop-while/issue-69841.rs b/tests/ui/for-loop-while/issue-69841.rs index 1aca16ca804..942b99b742b 100644 --- a/tests/ui/for-loop-while/issue-69841.rs +++ b/tests/ui/for-loop-while/issue-69841.rs @@ -2,7 +2,6 @@ // LLVM bug which needed a fix to be backported. // run-pass -// no-system-llvm fn main() { let buffer = [49u8, 10]; diff --git a/tests/ui/generic-associated-types/issue-79422.extended.stderr b/tests/ui/generic-associated-types/issue-79422.extended.stderr index 14492266cda..ae1526296a7 100644 --- a/tests/ui/generic-associated-types/issue-79422.extended.stderr +++ b/tests/ui/generic-associated-types/issue-79422.extended.stderr @@ -27,6 +27,7 @@ LL | type VRefCont<'a> = &'a V where Self: 'a; | ^^^^^ = note: expected trait object `(dyn RefCont<'_, u8> + 'static)` found reference `&u8` + = help: `&u8` implements `RefCont` so you could box the found value and coerce it to the trait object `Box<dyn RefCont>`, you will have to change the expected type as well = note: required for the cast from `Box<BTreeMap<u8, u8>>` to `Box<dyn MapLike<u8, u8, VRefCont = (dyn RefCont<'_, u8> + 'static)>>` error: aborting due to 2 previous errors diff --git a/tests/ui/impl-trait/dyn-impl-type-mismatch.rs b/tests/ui/impl-trait/dyn-impl-type-mismatch.rs new file mode 100644 index 00000000000..c6170abb582 --- /dev/null +++ b/tests/ui/impl-trait/dyn-impl-type-mismatch.rs @@ -0,0 +1,18 @@ +trait Trait {} +struct Struct; +impl Trait for Struct {} +fn foo() -> impl Trait { + Struct +} +fn main() { + let a: Box<dyn Trait> = if true { + Box::new(Struct) + } else { + foo() //~ ERROR E0308 + }; + let a: dyn Trait = if true { + Struct //~ ERROR E0308 + } else { + foo() //~ ERROR E0308 + }; +} diff --git a/tests/ui/impl-trait/dyn-impl-type-mismatch.stderr b/tests/ui/impl-trait/dyn-impl-type-mismatch.stderr new file mode 100644 index 00000000000..ddbdaf3eb4b --- /dev/null +++ b/tests/ui/impl-trait/dyn-impl-type-mismatch.stderr @@ -0,0 +1,43 @@ +error[E0308]: mismatched types + --> $DIR/dyn-impl-type-mismatch.rs:11:9 + | +LL | fn foo() -> impl Trait { + | ---------- the found opaque type +... +LL | foo() + | ^^^^^ expected `Box<dyn Trait>`, found opaque type + | + = note: expected struct `Box<dyn Trait>` + found opaque type `impl Trait` + = note: for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html +help: store this in the heap by calling `Box::new` + | +LL | Box::new(foo()) + | +++++++++ + + +error[E0308]: mismatched types + --> $DIR/dyn-impl-type-mismatch.rs:14:9 + | +LL | Struct + | ^^^^^^ expected `dyn Trait`, found `Struct` + | + = note: expected trait object `dyn Trait` + found struct `Struct` + = help: `Struct` implements `Trait` so you could box the found value and coerce it to the trait object `Box<dyn Trait>`, you will have to change the expected type as well + +error[E0308]: mismatched types + --> $DIR/dyn-impl-type-mismatch.rs:16:9 + | +LL | fn foo() -> impl Trait { + | ---------- the found opaque type +... +LL | foo() + | ^^^^^ expected `dyn Trait`, found opaque type + | + = note: expected trait object `dyn Trait` + found opaque type `impl Trait` + = help: you can box the `impl Trait` to coerce it to `Box<dyn Trait>`, but you'll have to change the expected type as well + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr b/tests/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr index 4a52f82540d..9cc199d1e0e 100644 --- a/tests/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr +++ b/tests/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr @@ -6,6 +6,7 @@ LL | fn fuz() -> (usize, Trait) { (42, Struct) } | = note: expected trait object `(dyn Trait + 'static)` found struct `Struct` + = help: `Struct` implements `Trait` so you could box the found value and coerce it to the trait object `Box<dyn Trait>`, you will have to change the expected type as well error[E0277]: the size for values of type `(dyn Trait + 'static)` cannot be known at compilation time --> $DIR/dyn-trait-return-should-be-impl-trait.rs:7:13 @@ -27,6 +28,7 @@ LL | fn bar() -> (usize, dyn Trait) { (42, Struct) } | = note: expected trait object `(dyn Trait + 'static)` found struct `Struct` + = help: `Struct` implements `Trait` so you could box the found value and coerce it to the trait object `Box<dyn Trait>`, you will have to change the expected type as well error[E0277]: the size for values of type `(dyn Trait + 'static)` cannot be known at compilation time --> $DIR/dyn-trait-return-should-be-impl-trait.rs:10:13 diff --git a/tests/ui/issue-76387-llvm-miscompile.rs b/tests/ui/issue-76387-llvm-miscompile.rs index a9b4686c970..a7fc9da6339 100644 --- a/tests/ui/issue-76387-llvm-miscompile.rs +++ b/tests/ui/issue-76387-llvm-miscompile.rs @@ -1,4 +1,3 @@ -// no-system-llvm // compile-flags: -C opt-level=3 // aux-build: issue-76387.rs // run-pass diff --git a/tests/ui/methods/issues/issue-61525.stderr b/tests/ui/methods/issues/issue-61525.stderr index a329b52e373..2670a3e4755 100644 --- a/tests/ui/methods/issues/issue-61525.stderr +++ b/tests/ui/methods/issues/issue-61525.stderr @@ -27,6 +27,7 @@ LL | 1.query::<dyn ToString>("") | = note: expected trait object `dyn ToString` found reference `&'static str` + = help: `&'static str` implements `ToString` so you could box the found value and coerce it to the trait object `Box<dyn ToString>`, you will have to change the expected type as well note: method defined here --> $DIR/issue-61525.rs:2:8 | diff --git a/tests/ui/privacy/private-type-in-interface.rs b/tests/ui/privacy/private-type-in-interface.rs index 39e0bf23cac..9f55127fd16 100644 --- a/tests/ui/privacy/private-type-in-interface.rs +++ b/tests/ui/privacy/private-type-in-interface.rs @@ -26,6 +26,7 @@ type A = <m::Alias as m::Trait>::X; //~ ERROR type `Priv` is private trait Tr2<T> {} impl<T> Tr2<T> for u8 {} fn g() -> impl Tr2<m::Alias> { 0 } //~ ERROR type `Priv` is private + //~| ERROR type `Priv` is private fn g_ext() -> impl Tr2<ext::Alias> { 0 } //~ ERROR type `ext::Priv` is private - + //~| ERROR type `ext::Priv` is private fn main() {} diff --git a/tests/ui/privacy/private-type-in-interface.stderr b/tests/ui/privacy/private-type-in-interface.stderr index 03225d84fdb..a5e80d6962d 100644 --- a/tests/ui/privacy/private-type-in-interface.stderr +++ b/tests/ui/privacy/private-type-in-interface.stderr @@ -46,11 +46,23 @@ error: type `Priv` is private LL | fn g() -> impl Tr2<m::Alias> { 0 } | ^^^^^^^^^^^^^^^^^^ private type +error: type `Priv` is private + --> $DIR/private-type-in-interface.rs:28:16 + | +LL | fn g() -> impl Tr2<m::Alias> { 0 } + | ^^^^^^^^^^^^^ private type + error: type `ext::Priv` is private - --> $DIR/private-type-in-interface.rs:29:15 + --> $DIR/private-type-in-interface.rs:30:15 | LL | fn g_ext() -> impl Tr2<ext::Alias> { 0 } | ^^^^^^^^^^^^^^^^^^^^ private type -error: aborting due to 9 previous errors +error: type `ext::Priv` is private + --> $DIR/private-type-in-interface.rs:30:20 + | +LL | fn g_ext() -> impl Tr2<ext::Alias> { 0 } + | ^^^^^^^^^^^^^^^ private type + +error: aborting due to 11 previous errors diff --git a/tests/ui/repeat-expr/repeat-to-run-dtor-twice.stderr b/tests/ui/repeat-expr/repeat-to-run-dtor-twice.stderr index f3fe8c10c02..1f7487e68be 100644 --- a/tests/ui/repeat-expr/repeat-to-run-dtor-twice.stderr +++ b/tests/ui/repeat-expr/repeat-to-run-dtor-twice.stderr @@ -6,7 +6,7 @@ LL | let _ = [ a; 5 ]; | = note: the `Copy` trait is required because this value will be copied for each element of the array = help: consider using `core::array::from_fn` to initialize the array - = help: see https://doc.rust-lang.org/stable/std/array/fn.from_fn.html# for more information + = help: see https://doc.rust-lang.org/stable/std/array/fn.from_fn.html for more information help: consider annotating `Foo` with `#[derive(Copy)]` | LL + #[derive(Copy)] diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/specializing-constness-2.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/specializing-constness-2.stderr index e5b9493b3ce..5210a694201 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/specializing-constness-2.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/specializing-constness-2.stderr @@ -7,16 +7,6 @@ LL | impl<T: Default> A for T { LL | impl<T: Default + ~const Sup> const A for T { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation -error[E0308]: mismatched types - --> $DIR/specializing-constness-2.rs:27:5 - | -LL | <T as A>::a(); - | ^^^^^^^^^^^^^ expected `host`, found `true` - | - = note: expected constant `host` - found constant `true` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0119, E0308. -For more information about an error, try `rustc --explain E0119`. +For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.rs b/tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.rs index 8ceecd801d3..a0ee7714417 100644 --- a/tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.rs +++ b/tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.rs @@ -6,7 +6,6 @@ struct S<const L: usize>; impl<const N: i32> Copy for S<N> {} -//~^ ERROR the constant `N` is not of type `usize` impl<const M: usize> Copy for S<M> {} //~^ ERROR: conflicting implementations of trait `Copy` for type `S<_>` diff --git a/tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.stderr b/tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.stderr index 3268a876733..2953bc95917 100644 --- a/tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.stderr +++ b/tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.stderr @@ -1,29 +1,11 @@ error[E0119]: conflicting implementations of trait `Copy` for type `S<_>` - --> $DIR/bad-const-wf-doesnt-specialize.rs:10:1 + --> $DIR/bad-const-wf-doesnt-specialize.rs:9:1 | LL | impl<const N: i32> Copy for S<N> {} | -------------------------------- first implementation here -LL | LL | impl<const M: usize> Copy for S<M> {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `S<_>` -error: the constant `N` is not of type `usize` - --> $DIR/bad-const-wf-doesnt-specialize.rs:8:29 - | -LL | impl<const N: i32> Copy for S<N> {} - | ^^^^ expected `usize`, found `i32` - | -note: required for `S<N>` to implement `Clone` - --> $DIR/bad-const-wf-doesnt-specialize.rs:5:10 - | -LL | #[derive(Clone)] - | ^^^^^ -LL | struct S<const L: usize>; - | ----- unsatisfied trait bound introduced in this `derive` macro -note: required by a bound in `Copy` - --> $SRC_DIR/core/src/marker.rs:LL:COL - = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/trait-bounds/issue-119530-sugg-from-fn.stderr b/tests/ui/trait-bounds/issue-119530-sugg-from-fn.stderr index f394c4cf027..9217637901b 100644 --- a/tests/ui/trait-bounds/issue-119530-sugg-from-fn.stderr +++ b/tests/ui/trait-bounds/issue-119530-sugg-from-fn.stderr @@ -6,7 +6,7 @@ LL | let string_arr = [foo(); 64]; | = note: the `Copy` trait is required because this value will be copied for each element of the array = help: consider using `core::array::from_fn` to initialize the array - = help: see https://doc.rust-lang.org/stable/std/array/fn.from_fn.html# for more information + = help: see https://doc.rust-lang.org/stable/std/array/fn.from_fn.html for more information error: aborting due to 1 previous error diff --git a/tests/ui/typeck/typeck_type_placeholder_item.rs b/tests/ui/typeck/typeck_type_placeholder_item.rs index f6d1fb6a23d..4eba14f5a93 100644 --- a/tests/ui/typeck/typeck_type_placeholder_item.rs +++ b/tests/ui/typeck/typeck_type_placeholder_item.rs @@ -198,7 +198,6 @@ trait Qux { //~^ ERROR the placeholder `_` is not allowed within types on item signatures for associated types } impl Qux for Struct { - //~^ ERROR: not all trait items implemented, missing: `F` type A = _; //~^ ERROR the placeholder `_` is not allowed within types on item signatures for associated types type B = _; diff --git a/tests/ui/typeck/typeck_type_placeholder_item.stderr b/tests/ui/typeck/typeck_type_placeholder_item.stderr index bfcc76c1dae..a4325b01f02 100644 --- a/tests/ui/typeck/typeck_type_placeholder_item.stderr +++ b/tests/ui/typeck/typeck_type_placeholder_item.stderr @@ -29,7 +29,7 @@ LL | struct BadStruct2<_, T>(_, T); | ^ expected identifier, found reserved identifier error: associated constant in `impl` without body - --> $DIR/typeck_type_placeholder_item.rs:206:5 + --> $DIR/typeck_type_placeholder_item.rs:205:5 | LL | const C: _; | ^^^^^^^^^^- @@ -411,7 +411,7 @@ LL | type Y = impl Trait<_>; | ^ not allowed in type signatures error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types - --> $DIR/typeck_type_placeholder_item.rs:217:31 + --> $DIR/typeck_type_placeholder_item.rs:216:31 | LL | fn value() -> Option<&'static _> { | ----------------^- @@ -420,7 +420,7 @@ LL | fn value() -> Option<&'static _> { | help: replace with the correct return type: `Option<&'static u8>` error[E0121]: the placeholder `_` is not allowed within types on item signatures for constants - --> $DIR/typeck_type_placeholder_item.rs:222:10 + --> $DIR/typeck_type_placeholder_item.rs:221:10 | LL | const _: Option<_> = map(value); | ^^^^^^^^^ @@ -429,7 +429,7 @@ LL | const _: Option<_> = map(value); | help: replace with the correct type: `Option<u8>` error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types - --> $DIR/typeck_type_placeholder_item.rs:225:31 + --> $DIR/typeck_type_placeholder_item.rs:224:31 | LL | fn evens_squared(n: usize) -> _ { | ^ @@ -438,13 +438,13 @@ LL | fn evens_squared(n: usize) -> _ { | help: replace with an appropriate return type: `impl Iterator<Item = usize>` error[E0121]: the placeholder `_` is not allowed within types on item signatures for constants - --> $DIR/typeck_type_placeholder_item.rs:230:10 + --> $DIR/typeck_type_placeholder_item.rs:229:10 | LL | const _: _ = (1..10).filter(|x| x % 2 == 0).map(|x| x * x); | ^ not allowed in type signatures | -note: however, the inferred type `Map<Filter<Range<i32>, {closure@typeck_type_placeholder_item.rs:230:29}>, {closure@typeck_type_placeholder_item.rs:230:49}>` cannot be named - --> $DIR/typeck_type_placeholder_item.rs:230:14 +note: however, the inferred type `Map<Filter<Range<i32>, {closure@typeck_type_placeholder_item.rs:229:29}>, {closure@typeck_type_placeholder_item.rs:229:49}>` cannot be named + --> $DIR/typeck_type_placeholder_item.rs:229:14 | LL | const _: _ = (1..10).filter(|x| x % 2 == 0).map(|x| x * x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -631,25 +631,25 @@ LL | fn clone_from(&mut self, other: &FnTest9) { *self = FnTest9; } | ~~~~~~~~ error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated types - --> $DIR/typeck_type_placeholder_item.rs:202:14 + --> $DIR/typeck_type_placeholder_item.rs:201:14 | LL | type A = _; | ^ not allowed in type signatures error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated types - --> $DIR/typeck_type_placeholder_item.rs:204:14 + --> $DIR/typeck_type_placeholder_item.rs:203:14 | LL | type B = _; | ^ not allowed in type signatures error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated constants - --> $DIR/typeck_type_placeholder_item.rs:206:14 + --> $DIR/typeck_type_placeholder_item.rs:205:14 | LL | const C: _; | ^ not allowed in type signatures error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated constants - --> $DIR/typeck_type_placeholder_item.rs:209:14 + --> $DIR/typeck_type_placeholder_item.rs:208:14 | LL | const D: _ = 42; | ^ @@ -657,16 +657,7 @@ LL | const D: _ = 42; | not allowed in type signatures | help: replace with the correct type: `i32` -error[E0046]: not all trait items implemented, missing: `F` - --> $DIR/typeck_type_placeholder_item.rs:200:1 - | -LL | type F: std::ops::Fn(_); - | ----------------------- `F` from trait -... -LL | impl Qux for Struct { - | ^^^^^^^^^^^^^^^^^^^ missing `F` in implementation - -error: aborting due to 72 previous errors +error: aborting due to 71 previous errors -Some errors have detailed explanations: E0046, E0121, E0282, E0403. -For more information about an error, try `rustc --explain E0046`. +Some errors have detailed explanations: E0121, E0282, E0403. +For more information about an error, try `rustc --explain E0121`. diff --git a/triagebot.toml b/triagebot.toml index 36e58f0f9d8..264f6efd982 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -646,6 +646,7 @@ compiler-team = [ "@estebank", "@oli-obk", "@wesleywiser", + "@michaelwoerister", ] compiler-team-contributors = [ "@TaKO8Ki", @@ -738,6 +739,10 @@ ast_lowering = [ "@estebank", "@spastorino", ] +debuginfo = [ + "@michaelwoerister", + "@davidtwco" +] fallback = [ "@Mark-Simulacrum" ] @@ -761,66 +766,71 @@ project-stable-mir = [ ] [assign.owners] -"/.github/workflows" = ["infra-ci"] -"/Cargo.lock" = ["@Mark-Simulacrum"] -"/Cargo.toml" = ["@Mark-Simulacrum"] -"/compiler" = ["compiler"] -"/compiler/rustc_arena" = ["compiler", "arena"] -"/compiler/rustc_ast" = ["compiler", "parser"] -"/compiler/rustc_ast_lowering" = ["compiler", "ast_lowering"] -"/compiler/rustc_hir_analysis" = ["compiler", "types"] -"/compiler/rustc_lexer" = ["compiler", "lexer"] -"/compiler/rustc_llvm" = ["@cuviper"] -"/compiler/rustc_middle/src/mir" = ["compiler", "mir"] -"/compiler/rustc_middle/src/traits" = ["compiler", "types"] -"/compiler/rustc_middle/src/ty" = ["compiler", "types"] -"/compiler/rustc_const_eval/src/interpret" = ["compiler", "mir"] -"/compiler/rustc_const_eval/src/transform" = ["compiler", "mir-opt"] -"/compiler/rustc_mir_build/src/build" = ["compiler", "mir"] -"/compiler/rustc_smir" = ["project-stable-mir"] -"/compiler/rustc_parse" = ["compiler", "parser"] -"/compiler/rustc_parse/src/lexer" = ["compiler", "lexer"] -"/compiler/rustc_query_impl" = ["compiler", "query-system"] -"/compiler/rustc_query_system" = ["compiler", "query-system"] -"/compiler/rustc_trait_selection" = ["compiler", "types"] -"/compiler/rustc_traits" = ["compiler", "types"] -"/compiler/rustc_type_ir" = ["compiler", "types"] -"/compiler/stable_mir" = ["project-stable-mir"] -"/library/alloc" = ["libs"] -"/library/core" = ["libs"] -"/library/panic_abort" = ["libs"] -"/library/panic_unwind" = ["libs"] -"/library/proc_macro" = ["@petrochenkov"] -"/library/std" = ["libs"] -"/library/std/src/sys/pal/windows" = ["@ChrisDenton", "@thomcc"] -"/library/stdarch" = ["libs"] -"/library/test" = ["libs"] -"/src/bootstrap" = ["bootstrap"] -"/src/ci" = ["infra-ci"] -"/src/doc" = ["docs"] -"/src/doc/book" = ["@ehuss"] -"/src/doc/edition-guide" = ["@ehuss"] -"/src/doc/embedded-book" = ["@ehuss"] -"/src/doc/nomicon" = ["@ehuss"] -"/src/doc/reference" = ["@ehuss"] -"/src/doc/rust-by-example" = ["@ehuss"] -"/src/doc/rustc-dev-guide" = ["@ehuss"] -"/src/doc/rustdoc" = ["rustdoc"] -"/src/doc/style-guide" = ["style-team"] -"/src/etc" = ["@Mark-Simulacrum"] -"/src/librustdoc" = ["rustdoc"] -"/src/llvm-project" = ["@cuviper"] -"/src/rustdoc-json-types" = ["rustdoc"] -"/src/stage0.json" = ["bootstrap"] -"/tests/ui" = ["compiler"] -"/src/tools/cargo" = ["@ehuss", "@joshtriplett"] -"/src/tools/compiletest" = ["bootstrap", "@wesleywiser", "@oli-obk", "@compiler-errors"] -"/src/tools/linkchecker" = ["@ehuss"] -"/src/tools/rust-installer" = ["bootstrap"] -"/src/tools/rustbook" = ["@ehuss"] -"/src/tools/rustdoc" = ["rustdoc"] -"/src/tools/rustdoc-js" = ["rustdoc"] -"/src/tools/rustdoc-themes" = ["rustdoc"] -"/src/tools/tidy" = ["bootstrap"] -"/src/tools/x" = ["bootstrap"] -"/src/tools/rustdoc-gui-test" = ["bootstrap", "@onur-ozkan"] +"/.github/workflows" = ["infra-ci"] +"/Cargo.lock" = ["@Mark-Simulacrum"] +"/Cargo.toml" = ["@Mark-Simulacrum"] +"/compiler" = ["compiler"] +"/compiler/rustc_arena" = ["compiler", "arena"] +"/compiler/rustc_ast" = ["compiler", "parser"] +"/compiler/rustc_ast_lowering" = ["compiler", "ast_lowering"] +"/compiler/rustc_data_structures/src/stable_hasher.rs" = ["compiler", "incremental"] +"/compiler/rustc_hir_analysis" = ["compiler", "types"] +"/compiler/rustc_incremental" = ["compiler", "incremental"] +"/compiler/rustc_lexer" = ["compiler", "lexer"] +"/compiler/rustc_llvm" = ["@cuviper"] +"/compiler/rustc_codegen_llvm/src/debuginfo" = ["compiler", "debuginfo"] +"/compiler/rustc_middle/src/mir" = ["compiler", "mir"] +"/compiler/rustc_middle/src/traits" = ["compiler", "types"] +"/compiler/rustc_middle/src/ty" = ["compiler", "types"] +"/compiler/rustc_const_eval/src/interpret" = ["compiler", "mir"] +"/compiler/rustc_const_eval/src/transform" = ["compiler", "mir-opt"] +"/compiler/rustc_mir_build/src/build" = ["compiler", "mir"] +"/compiler/rustc_smir" = ["project-stable-mir"] +"/compiler/rustc_parse" = ["compiler", "parser"] +"/compiler/rustc_parse/src/lexer" = ["compiler", "lexer"] +"/compiler/rustc_query_impl" = ["compiler", "query-system"] +"/compiler/rustc_query_system" = ["compiler", "query-system"] +"/compiler/rustc_query_system/src/dep_graph" = ["compiler", "incremental", "query-system"] +"/compiler/rustc_query_system/src/ich" = ["compiler", "incremental", "query-system"] +"/compiler/rustc_trait_selection" = ["compiler", "types"] +"/compiler/rustc_traits" = ["compiler", "types"] +"/compiler/rustc_type_ir" = ["compiler", "types"] +"/compiler/stable_mir" = ["project-stable-mir"] +"/library/alloc" = ["libs"] +"/library/core" = ["libs"] +"/library/panic_abort" = ["libs"] +"/library/panic_unwind" = ["libs"] +"/library/proc_macro" = ["@petrochenkov"] +"/library/std" = ["libs"] +"/library/std/src/sys/pal/windows" = ["@ChrisDenton", "@thomcc"] +"/library/stdarch" = ["libs"] +"/library/test" = ["libs"] +"/src/bootstrap" = ["bootstrap"] +"/src/ci" = ["infra-ci"] +"/src/doc" = ["docs"] +"/src/doc/book" = ["@ehuss"] +"/src/doc/edition-guide" = ["@ehuss"] +"/src/doc/embedded-book" = ["@ehuss"] +"/src/doc/nomicon" = ["@ehuss"] +"/src/doc/reference" = ["@ehuss"] +"/src/doc/rust-by-example" = ["@ehuss"] +"/src/doc/rustc-dev-guide" = ["@ehuss"] +"/src/doc/rustdoc" = ["rustdoc"] +"/src/doc/style-guide" = ["style-team"] +"/src/etc" = ["@Mark-Simulacrum"] +"/src/librustdoc" = ["rustdoc"] +"/src/llvm-project" = ["@cuviper"] +"/src/rustdoc-json-types" = ["rustdoc"] +"/src/stage0.json" = ["bootstrap"] +"/tests/ui" = ["compiler"] +"/src/tools/cargo" = ["@ehuss", "@joshtriplett"] +"/src/tools/compiletest" = ["bootstrap", "@wesleywiser", "@oli-obk", "@compiler-errors"] +"/src/tools/linkchecker" = ["@ehuss"] +"/src/tools/rust-installer" = ["bootstrap"] +"/src/tools/rustbook" = ["@ehuss"] +"/src/tools/rustdoc" = ["rustdoc"] +"/src/tools/rustdoc-js" = ["rustdoc"] +"/src/tools/rustdoc-themes" = ["rustdoc"] +"/src/tools/tidy" = ["bootstrap"] +"/src/tools/x" = ["bootstrap"] +"/src/tools/rustdoc-gui-test" = ["bootstrap", "@onur-ozkan"] |
