diff --git a/compiler/rustc_data_structures/src/sip128.rs b/compiler/rustc_data_structures/src/sip128.rs index beb28dd072058..2c4eff618c685 100644 --- a/compiler/rustc_data_structures/src/sip128.rs +++ b/compiler/rustc_data_structures/src/sip128.rs @@ -125,15 +125,28 @@ impl SipHasher128 { // A specialized write function for values with size <= 8. // - // The hashing of multi-byte integers depends on endianness. E.g.: + // The input must be zero-extended to 64-bits by the caller. This extension + // isn't hashed, but the implementation requires it for correctness. + // + // This function, given the same integer size and value, has the same effect + // on both little- and big-endian hardware. It operates on values without + // depending on their sequence in memory, so is independent of endianness. + // + // However, we want SipHasher128 to be platform-dependent, in order to be + // consistent with the platform-dependent SipHasher in libstd. In other + // words, we want: + // // - little-endian: `write_u32(0xDDCCBBAA)` == `write([0xAA, 0xBB, 0xCC, 0xDD])` // - big-endian: `write_u32(0xDDCCBBAA)` == `write([0xDD, 0xCC, 0xBB, 0xAA])` // - // This function does the right thing for little-endian hardware. On - // big-endian hardware `x` must be byte-swapped first to give the right - // behaviour. After any byte-swapping, the input must be zero-extended to - // 64-bits. The caller is responsible for the byte-swapping and - // zero-extension. + // Therefore, in order to produce endian-dependent results, SipHasher128's + // `write_xxx` Hasher trait methods byte-swap `x` prior to zero-extending. + // + // If clients of SipHasher128 itself want platform-independent results, they + // *also* must byte-swap integer inputs before invoking the `write_xxx` + // methods on big-endian hardware (that is, two byte-swaps must occur--one + // in the client, and one in SipHasher128). Additionally, they must extend + // `usize` and `isize` types to 64 bits on 32-bit systems. #[inline] fn short_write(&mut self, _x: T, x: u64) { let size = mem::size_of::(); diff --git a/compiler/rustc_data_structures/src/sip128/tests.rs b/compiler/rustc_data_structures/src/sip128/tests.rs index 80b7fc7475610..2e2274a7b775e 100644 --- a/compiler/rustc_data_structures/src/sip128/tests.rs +++ b/compiler/rustc_data_structures/src/sip128/tests.rs @@ -1,7 +1,6 @@ use super::*; use std::hash::{Hash, Hasher}; -use std::{mem, slice}; // Hash just the bytes of the slice, without length prefix struct Bytes<'a>(&'a [u8]); @@ -399,20 +398,55 @@ fn test_hash_no_concat_alias() { } #[test] -fn test_write_short_works() { - let test_usize = 0xd0c0b0a0usize; +fn test_short_write_works() { + let test_u8 = 0xFF_u8; + let test_u16 = 0x1122_u16; + let test_u32 = 0x22334455_u32; + let test_u64 = 0x33445566_778899AA_u64; + let test_u128 = 0x11223344_55667788_99AABBCC_DDEEFF77_u128; + let test_usize = 0xD0C0B0A0_usize; + + let test_i8 = -1_i8; + let test_i16 = -2_i16; + let test_i32 = -3_i32; + let test_i64 = -4_i64; + let test_i128 = -5_i128; + let test_isize = -6_isize; + let mut h1 = SipHasher128::new_with_keys(0, 0); - h1.write_usize(test_usize); h1.write(b"bytes"); h1.write(b"string"); - h1.write_u8(0xFFu8); - h1.write_u8(0x01u8); + h1.write_u8(test_u8); + h1.write_u16(test_u16); + h1.write_u32(test_u32); + h1.write_u64(test_u64); + h1.write_u128(test_u128); + h1.write_usize(test_usize); + h1.write_i8(test_i8); + h1.write_i16(test_i16); + h1.write_i32(test_i32); + h1.write_i64(test_i64); + h1.write_i128(test_i128); + h1.write_isize(test_isize); + let mut h2 = SipHasher128::new_with_keys(0, 0); - h2.write(unsafe { - slice::from_raw_parts(&test_usize as *const _ as *const u8, mem::size_of::()) - }); h2.write(b"bytes"); h2.write(b"string"); - h2.write(&[0xFFu8, 0x01u8]); - assert_eq!(h1.finish128(), h2.finish128()); + h2.write(&test_u8.to_ne_bytes()); + h2.write(&test_u16.to_ne_bytes()); + h2.write(&test_u32.to_ne_bytes()); + h2.write(&test_u64.to_ne_bytes()); + h2.write(&test_u128.to_ne_bytes()); + h2.write(&test_usize.to_ne_bytes()); + h2.write(&test_i8.to_ne_bytes()); + h2.write(&test_i16.to_ne_bytes()); + h2.write(&test_i32.to_ne_bytes()); + h2.write(&test_i64.to_ne_bytes()); + h2.write(&test_i128.to_ne_bytes()); + h2.write(&test_isize.to_ne_bytes()); + + let h1_hash = h1.finish128(); + let h2_hash = h2.finish128(); + + assert_eq!(h1_hash, h2_hash); } diff --git a/compiler/rustc_data_structures/src/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs index c1c79b174f415..68875b3fbde9b 100644 --- a/compiler/rustc_data_structures/src/stable_hasher.rs +++ b/compiler/rustc_data_structures/src/stable_hasher.rs @@ -5,6 +5,9 @@ use smallvec::SmallVec; use std::hash::{BuildHasher, Hash, Hasher}; use std::mem; +#[cfg(test)] +mod tests; + /// When hashing something that ends up affecting properties like symbol names, /// we want these symbol names to be calculated independently of other factors /// like what architecture you're compiling *from*. @@ -129,7 +132,8 @@ impl Hasher for StableHasher { fn write_isize(&mut self, i: isize) { // Always treat isize as i64 so we get the same results on 32 and 64 bit // platforms. This is important for symbol hashes when cross compiling, - // for example. + // for example. Sign extending here is preferable as it means that the + // same negative number hashes the same on both 32 and 64 bit platforms. self.state.write_i64((i as i64).to_le()); } } diff --git a/compiler/rustc_data_structures/src/stable_hasher/tests.rs b/compiler/rustc_data_structures/src/stable_hasher/tests.rs new file mode 100644 index 0000000000000..cd6ff96a555f4 --- /dev/null +++ b/compiler/rustc_data_structures/src/stable_hasher/tests.rs @@ -0,0 +1,73 @@ +use super::*; + +// The tests below compare the computed hashes to particular expected values +// in order to test that we produce the same results on different platforms, +// regardless of endianness and `usize` and `isize` size differences (this +// of course assumes we run these tests on platforms that differ in those +// ways). The expected values depend on the hashing algorithm used, so they +// need to be updated whenever StableHasher changes its hashing algorithm. + +#[test] +fn test_hash_integers() { + // Test that integers are handled consistently across platforms. + let test_u8 = 0xAB_u8; + let test_u16 = 0xFFEE_u16; + let test_u32 = 0x445577AA_u32; + let test_u64 = 0x01234567_13243546_u64; + let test_u128 = 0x22114433_66557788_99AACCBB_EEDDFF77_u128; + let test_usize = 0xD0C0B0A0_usize; + + let test_i8 = -100_i8; + let test_i16 = -200_i16; + let test_i32 = -300_i32; + let test_i64 = -400_i64; + let test_i128 = -500_i128; + let test_isize = -600_isize; + + let mut h = StableHasher::new(); + test_u8.hash(&mut h); + test_u16.hash(&mut h); + test_u32.hash(&mut h); + test_u64.hash(&mut h); + test_u128.hash(&mut h); + test_usize.hash(&mut h); + test_i8.hash(&mut h); + test_i16.hash(&mut h); + test_i32.hash(&mut h); + test_i64.hash(&mut h); + test_i128.hash(&mut h); + test_isize.hash(&mut h); + + // This depends on the hashing algorithm. See note at top of file. + let expected = (2736651863462566372, 8121090595289675650); + + assert_eq!(h.finalize(), expected); +} + +#[test] +fn test_hash_usize() { + // Test that usize specifically is handled consistently across platforms. + let test_usize = 0xABCDEF01_usize; + + let mut h = StableHasher::new(); + test_usize.hash(&mut h); + + // This depends on the hashing algorithm. See note at top of file. + let expected = (5798740672699530587, 11186240177685111648); + + assert_eq!(h.finalize(), expected); +} + +#[test] +fn test_hash_isize() { + // Test that isize specifically is handled consistently across platforms. + let test_isize = -7_isize; + + let mut h = StableHasher::new(); + test_isize.hash(&mut h); + + // This depends on the hashing algorithm. See note at top of file. + let expected = (14721296605626097289, 11385941877786388409); + + assert_eq!(h.finalize(), expected); +} diff --git a/compiler/rustc_mir/src/borrow_check/type_check/constraint_conversion.rs b/compiler/rustc_mir/src/borrow_check/type_check/constraint_conversion.rs index 711271a63fbff..8513e5e531bd7 100644 --- a/compiler/rustc_mir/src/borrow_check/type_check/constraint_conversion.rs +++ b/compiler/rustc_mir/src/borrow_check/type_check/constraint_conversion.rs @@ -62,8 +62,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { // `self.constraints`, but we also want to be mutating // `self.member_constraints`. For now, just swap out the value // we want and replace at the end. - let mut tmp = - std::mem::replace(&mut self.constraints.member_constraints, Default::default()); + let mut tmp = std::mem::take(&mut self.constraints.member_constraints); for member_constraint in member_constraints { tmp.push_constraint(member_constraint, |r| self.to_region_vid(r)); } diff --git a/compiler/rustc_mir/src/transform/check_const_item_mutation.rs b/compiler/rustc_mir/src/transform/check_const_item_mutation.rs index 70c1aed0957f8..0281c478a6ca0 100644 --- a/compiler/rustc_mir/src/transform/check_const_item_mutation.rs +++ b/compiler/rustc_mir/src/transform/check_const_item_mutation.rs @@ -60,11 +60,15 @@ impl<'a, 'tcx> Visitor<'tcx> for ConstMutationChecker<'a, 'tcx> { // so emitting a lint would be redundant. if !lhs.projection.is_empty() { if let Some(def_id) = self.is_const_item(lhs.local) { - self.lint_const_item_usage(def_id, loc, |lint| { - let mut lint = lint.build("attempting to modify a `const` item"); - lint.note("each usage of a `const` item creates a new temporary - the original `const` item will not be modified"); - lint - }) + // Don't lint on writes through a pointer + // (e.g. `unsafe { *FOO = 0; *BAR.field = 1; }`) + if !matches!(lhs.projection.last(), Some(PlaceElem::Deref)) { + self.lint_const_item_usage(def_id, loc, |lint| { + let mut lint = lint.build("attempting to modify a `const` item"); + lint.note("each usage of a `const` item creates a new temporary - the original `const` item will not be modified"); + lint + }) + } } } // We are looking for MIR of the form: diff --git a/compiler/rustc_mir/src/transform/early_otherwise_branch.rs b/compiler/rustc_mir/src/transform/early_otherwise_branch.rs index 67e679a8b08d0..7a9089d0f3675 100644 --- a/compiler/rustc_mir/src/transform/early_otherwise_branch.rs +++ b/compiler/rustc_mir/src/transform/early_otherwise_branch.rs @@ -91,7 +91,7 @@ impl<'tcx> MirPass<'tcx> for EarlyOtherwiseBranch { let not_equal_rvalue = Rvalue::BinaryOp( not_equal, Operand::Copy(Place::from(second_discriminant_temp)), - Operand::Copy(Place::from(first_descriminant_place)), + Operand::Copy(first_descriminant_place), ); patch.add_statement( end_of_block_location, diff --git a/compiler/rustc_mir/src/transform/promote_consts.rs b/compiler/rustc_mir/src/transform/promote_consts.rs index cd361e430fa61..89f7531b3a7ff 100644 --- a/compiler/rustc_mir/src/transform/promote_consts.rs +++ b/compiler/rustc_mir/src/transform/promote_consts.rs @@ -137,7 +137,7 @@ fn args_required_const(tcx: TyCtxt<'_>, def_id: DefId) -> Option> { LitKind::Int(a, _) => { ret.push(a as usize); } - _ => return None, + _ => bug!("invalid arg index"), } } Some(ret) diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index efe947daa2866..1ec251326a8f6 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -8,12 +8,12 @@ use rustc_middle::hir::map::Map; use rustc_middle::ty::query::Providers; use rustc_middle::ty::TyCtxt; -use rustc_ast::{Attribute, NestedMetaItem}; -use rustc_errors::struct_span_err; +use rustc_ast::{Attribute, LitKind, NestedMetaItem}; +use rustc_errors::{pluralize, struct_span_err}; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; -use rustc_hir::{self, HirId, Item, ItemKind, TraitItem}; +use rustc_hir::{self, FnSig, ForeignItem, ForeignItemKind, HirId, Item, ItemKind, TraitItem}; use rustc_hir::{MethodKind, Target}; use rustc_session::lint::builtin::{CONFLICTING_REPR_HINTS, UNUSED_ATTRIBUTES}; use rustc_session::parse::feature_err; @@ -43,6 +43,12 @@ pub(crate) fn target_from_impl_item<'tcx>( } } +#[derive(Clone, Copy)] +enum ItemLike<'tcx> { + Item(&'tcx Item<'tcx>), + ForeignItem(&'tcx ForeignItem<'tcx>), +} + struct CheckAttrVisitor<'tcx> { tcx: TyCtxt<'tcx>, } @@ -55,7 +61,7 @@ impl CheckAttrVisitor<'tcx> { attrs: &'hir [Attribute], span: &Span, target: Target, - item: Option<&Item<'_>>, + item: Option>, ) { let mut is_valid = true; for attr in attrs { @@ -75,6 +81,8 @@ impl CheckAttrVisitor<'tcx> { self.check_no_link(&attr, span, target) } else if self.tcx.sess.check_name(attr, sym::export_name) { self.check_export_name(&attr, span, target) + } else if self.tcx.sess.check_name(attr, sym::rustc_args_required_const) { + self.check_rustc_args_required_const(&attr, span, target, item) } else { // lint-only checks if self.tcx.sess.check_name(attr, sym::cold) { @@ -400,6 +408,71 @@ impl CheckAttrVisitor<'tcx> { } } + /// Checks if `#[rustc_args_required_const]` is applied to a function and has a valid argument. + fn check_rustc_args_required_const( + &self, + attr: &Attribute, + span: &Span, + target: Target, + item: Option>, + ) -> bool { + if let Target::Fn | Target::Method(..) | Target::ForeignFn = target { + let mut invalid_args = vec![]; + for meta in attr.meta_item_list().expect("no meta item list") { + if let Some(LitKind::Int(val, _)) = meta.literal().map(|lit| &lit.kind) { + if let Some(ItemLike::Item(Item { + kind: ItemKind::Fn(FnSig { decl, .. }, ..), + .. + })) + | Some(ItemLike::ForeignItem(ForeignItem { + kind: ForeignItemKind::Fn(decl, ..), + .. + })) = item + { + let arg_count = decl.inputs.len() as u128; + if *val >= arg_count { + let span = meta.span(); + self.tcx + .sess + .struct_span_err(span, "index exceeds number of arguments") + .span_label( + span, + format!( + "there {} only {} argument{}", + if arg_count != 1 { "are" } else { "is" }, + arg_count, + pluralize!(arg_count) + ), + ) + .emit(); + return false; + } + } else { + bug!("should be a function item"); + } + } else { + invalid_args.push(meta.span()); + } + } + if !invalid_args.is_empty() { + self.tcx + .sess + .struct_span_err(invalid_args, "arguments should be non-negative integers") + .emit(); + false + } else { + true + } + } else { + self.tcx + .sess + .struct_span_err(attr.span, "attribute should be applied to a function") + .span_label(*span, "not a function") + .emit(); + false + } + } + /// Checks if `#[link_section]` is applied to a function or static. fn check_link_section(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) { match target { @@ -448,7 +521,7 @@ impl CheckAttrVisitor<'tcx> { attrs: &'hir [Attribute], span: &Span, target: Target, - item: Option<&Item<'_>>, + item: Option>, hir_id: HirId, ) { // Extract the names of all repr hints, e.g., [foo, bar, align] for: @@ -564,7 +637,14 @@ impl CheckAttrVisitor<'tcx> { // Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8) if (int_reprs > 1) || (is_simd && is_c) - || (int_reprs == 1 && is_c && item.map_or(false, |item| is_c_like_enum(item))) + || (int_reprs == 1 + && is_c + && item.map_or(false, |item| { + if let ItemLike::Item(item) = item { + return is_c_like_enum(item); + } + return false; + })) { self.tcx.struct_span_lint_hir( CONFLICTING_REPR_HINTS, @@ -649,7 +729,13 @@ impl Visitor<'tcx> for CheckAttrVisitor<'tcx> { fn visit_item(&mut self, item: &'tcx Item<'tcx>) { let target = Target::from_item(item); - self.check_attributes(item.hir_id, item.attrs, &item.span, target, Some(item)); + self.check_attributes( + item.hir_id, + item.attrs, + &item.span, + target, + Some(ItemLike::Item(item)), + ); intravisit::walk_item(self, item) } @@ -659,9 +745,15 @@ impl Visitor<'tcx> for CheckAttrVisitor<'tcx> { intravisit::walk_trait_item(self, trait_item) } - fn visit_foreign_item(&mut self, f_item: &'tcx hir::ForeignItem<'tcx>) { + fn visit_foreign_item(&mut self, f_item: &'tcx ForeignItem<'tcx>) { let target = Target::from_foreign_item(f_item); - self.check_attributes(f_item.hir_id, &f_item.attrs, &f_item.span, target, None); + self.check_attributes( + f_item.hir_id, + &f_item.attrs, + &f_item.span, + target, + Some(ItemLike::ForeignItem(f_item)), + ); intravisit::walk_foreign_item(self, f_item) } diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 0cfcaca906033..3828cf4d30210 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -23,6 +23,9 @@ use rustc_session::lint; use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::Span; +use std::cmp; + +/// Check if a given constant can be evaluated. pub fn is_const_evaluatable<'cx, 'tcx>( infcx: &InferCtxt<'cx, 'tcx>, def: ty::WithOptConstParam, @@ -32,23 +35,87 @@ pub fn is_const_evaluatable<'cx, 'tcx>( ) -> Result<(), ErrorHandled> { debug!("is_const_evaluatable({:?}, {:?})", def, substs); if infcx.tcx.features().const_evaluatable_checked { - if let Some(ct) = AbstractConst::new(infcx.tcx, def, substs)? { - for pred in param_env.caller_bounds() { - match pred.skip_binders() { - ty::PredicateAtom::ConstEvaluatable(b_def, b_substs) => { - debug!("is_const_evaluatable: caller_bound={:?}, {:?}", b_def, b_substs); - if b_def == def && b_substs == substs { - debug!("is_const_evaluatable: caller_bound ~~> ok"); - return Ok(()); - } else if AbstractConst::new(infcx.tcx, b_def, b_substs)? - .map_or(false, |b_ct| try_unify(infcx.tcx, ct, b_ct)) - { - debug!("is_const_evaluatable: abstract_const ~~> ok"); - return Ok(()); + let tcx = infcx.tcx; + match AbstractConst::new(tcx, def, substs)? { + // We are looking at a generic abstract constant. + Some(ct) => { + for pred in param_env.caller_bounds() { + match pred.skip_binders() { + ty::PredicateAtom::ConstEvaluatable(b_def, b_substs) => { + debug!( + "is_const_evaluatable: caller_bound={:?}, {:?}", + b_def, b_substs + ); + if b_def == def && b_substs == substs { + debug!("is_const_evaluatable: caller_bound ~~> ok"); + return Ok(()); + } else if AbstractConst::new(tcx, b_def, b_substs)? + .map_or(false, |b_ct| try_unify(tcx, ct, b_ct)) + { + debug!("is_const_evaluatable: abstract_const ~~> ok"); + return Ok(()); + } } + _ => {} // don't care } - _ => {} // don't care } + + // We were unable to unify the abstract constant with + // a constant found in the caller bounds, there are + // now three possible cases here. + // + // - The substs are concrete enough that we can simply + // try and evaluate the given constant. + // - The abstract const still references an inference + // variable, in this case we return `TooGeneric`. + // - The abstract const references a generic parameter, + // this means that we emit an error here. + #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] + enum FailureKind { + MentionsInfer, + MentionsParam, + Concrete, + } + let mut failure_kind = FailureKind::Concrete; + walk_abstract_const(tcx, ct, |node| match node { + Node::Leaf(leaf) => { + let leaf = leaf.subst(tcx, ct.substs); + if leaf.has_infer_types_or_consts() { + failure_kind = FailureKind::MentionsInfer; + } else if leaf.has_param_types_or_consts() { + failure_kind = cmp::min(failure_kind, FailureKind::MentionsParam); + } + } + Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => (), + }); + + match failure_kind { + FailureKind::MentionsInfer => { + return Err(ErrorHandled::TooGeneric); + } + FailureKind::MentionsParam => { + // FIXME(const_evaluatable_checked): Better error message. + infcx + .tcx + .sess + .struct_span_err(span, "unconstrained generic constant") + .span_help( + tcx.def_span(def.did), + "consider adding a `where` bound for this expression", + ) + .emit(); + return Err(ErrorHandled::Reported(ErrorReported)); + } + FailureKind::Concrete => { + // Dealt with below by the same code which handles this + // without the feature gate. + } + } + } + None => { + // If we are dealing with a concrete constant, we can + // reuse the old code path and try to evaluate + // the constant. } } } @@ -95,7 +162,36 @@ pub fn is_const_evaluatable<'cx, 'tcx>( } debug!(?concrete, "is_const_evaluatable"); - concrete.map(drop) + match concrete { + Err(ErrorHandled::TooGeneric) if !substs.has_infer_types_or_consts() => { + // FIXME(const_evaluatable_checked): We really should move + // emitting this error message to fulfill instead. For + // now this is easier. + // + // This is not a problem without `const_evaluatable_checked` as + // all `ConstEvaluatable` predicates have to be fulfilled for compilation + // to succeed. + // + // @lcnr: We already emit an error for things like + // `fn test() -> [0 - N]` eagerly here, + // so until we fix this I don't really care. + + let mut err = infcx + .tcx + .sess + .struct_span_err(span, "constant expression depends on a generic parameter"); + // FIXME(const_generics): we should suggest to the user how they can resolve this + // issue. However, this is currently not actually possible + // (see https://github.com/rust-lang/rust/issues/66962#issuecomment-575907083). + // + // Note that with `feature(const_evaluatable_checked)` this case should not + // be reachable. + err.note("this may fail depending on what value the parameter takes"); + err.emit(); + Err(ErrorHandled::Reported(ErrorReported)) + } + c => c.map(drop), + } } /// A tree representing an anonymous constant. @@ -421,6 +517,33 @@ pub(super) fn try_unify_abstract_consts<'tcx>( // on `ErrorReported`. } +fn walk_abstract_const<'tcx, F>(tcx: TyCtxt<'tcx>, ct: AbstractConst<'tcx>, mut f: F) +where + F: FnMut(Node<'tcx>), +{ + recurse(tcx, ct, &mut f); + fn recurse<'tcx>(tcx: TyCtxt<'tcx>, ct: AbstractConst<'tcx>, f: &mut dyn FnMut(Node<'tcx>)) { + let root = ct.root(); + f(root); + match root { + Node::Leaf(_) => (), + Node::Binop(_, l, r) => { + recurse(tcx, ct.subtree(l), f); + recurse(tcx, ct.subtree(r), f); + } + Node::UnaryOp(_, v) => { + recurse(tcx, ct.subtree(v), f); + } + Node::FunctionCall(func, args) => { + recurse(tcx, ct.subtree(func), f); + for &arg in args { + recurse(tcx, ct.subtree(arg), f); + } + } + } + } +} + /// Tries to unify two abstract constants using structural equality. pub(super) fn try_unify<'tcx>( tcx: TyCtxt<'tcx>, diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 1b234a1535c97..cb3de57cfed07 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -745,25 +745,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let violations = self.tcx.object_safety_violations(did); report_object_safety_error(self.tcx, span, did, violations) } - ConstEvalFailure(ErrorHandled::TooGeneric) => { - // In this instance, we have a const expression containing an unevaluated - // generic parameter. We have no idea whether this expression is valid or - // not (e.g. it might result in an error), but we don't want to just assume - // that it's okay, because that might result in post-monomorphisation time - // errors. The onus is really on the caller to provide values that it can - // prove are well-formed. - let mut err = self - .tcx - .sess - .struct_span_err(span, "constant expression depends on a generic parameter"); - // FIXME(const_generics): we should suggest to the user how they can resolve this - // issue. However, this is currently not actually possible - // (see https://github.com/rust-lang/rust/issues/66962#issuecomment-575907083). - err.note("this may fail depending on what value the parameter takes"); - err + bug!("too generic should have been handled in `is_const_evaluatable`"); } - // Already reported in the query. ConstEvalFailure(ErrorHandled::Reported(ErrorReported)) => { // FIXME(eddyb) remove this once `ErrorReported` becomes a proof token. diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index fdebd88a086c7..8586a550230f9 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -496,6 +496,13 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { obligation.cause.span, ) { Ok(()) => ProcessResult::Changed(vec![]), + Err(ErrorHandled::TooGeneric) => { + pending_obligation.stalled_on = substs + .iter() + .filter_map(|ty| TyOrConstInferVar::maybe_from_generic_arg(ty)) + .collect(); + ProcessResult::Unchanged + } Err(e) => ProcessResult::Error(CodeSelectionError(ConstEvalFailure(e))), } } @@ -537,8 +544,10 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { Err(ErrorHandled::TooGeneric) => { stalled_on.append( &mut substs - .types() - .filter_map(|ty| TyOrConstInferVar::maybe_from_ty(ty)) + .iter() + .filter_map(|arg| { + TyOrConstInferVar::maybe_from_generic_arg(arg) + }) .collect(), ); Err(ErrorHandled::TooGeneric) diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index a4943231dfdff..9cb5c232646f8 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -7,14 +7,19 @@ //! [rustc dev guide]:https://rustc-dev-guide.rust-lang.org/traits/resolution.html#candidate-assembly use rustc_hir as hir; use rustc_infer::traits::{Obligation, SelectionError, TraitObligation}; +use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, TypeFoldable}; use rustc_target::spec::abi::Abi; +use crate::traits::coherence::Conflict; use crate::traits::{util, SelectionResult}; +use crate::traits::{Overflow, Unimplemented}; use super::BuiltinImplConditions; +use super::IntercrateAmbiguityCause; +use super::OverflowError; use super::SelectionCandidate::{self, *}; -use super::{SelectionCandidateSet, SelectionContext, TraitObligationStack}; +use super::{EvaluatedCandidate, SelectionCandidateSet, SelectionContext, TraitObligationStack}; impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { pub(super) fn candidate_from_obligation<'o>( @@ -62,6 +67,161 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { candidate } + fn candidate_from_obligation_no_cache<'o>( + &mut self, + stack: &TraitObligationStack<'o, 'tcx>, + ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { + if let Some(conflict) = self.is_knowable(stack) { + debug!("coherence stage: not knowable"); + if self.intercrate_ambiguity_causes.is_some() { + debug!("evaluate_stack: intercrate_ambiguity_causes is some"); + // Heuristics: show the diagnostics when there are no candidates in crate. + if let Ok(candidate_set) = self.assemble_candidates(stack) { + let mut no_candidates_apply = true; + + for c in candidate_set.vec.iter() { + if self.evaluate_candidate(stack, &c)?.may_apply() { + no_candidates_apply = false; + break; + } + } + + if !candidate_set.ambiguous && no_candidates_apply { + let trait_ref = stack.obligation.predicate.skip_binder().trait_ref; + let self_ty = trait_ref.self_ty(); + let (trait_desc, self_desc) = with_no_trimmed_paths(|| { + let trait_desc = trait_ref.print_only_trait_path().to_string(); + let self_desc = if self_ty.has_concrete_skeleton() { + Some(self_ty.to_string()) + } else { + None + }; + (trait_desc, self_desc) + }); + let cause = if let Conflict::Upstream = conflict { + IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_desc, self_desc } + } else { + IntercrateAmbiguityCause::DownstreamCrate { trait_desc, self_desc } + }; + debug!("evaluate_stack: pushing cause = {:?}", cause); + self.intercrate_ambiguity_causes.as_mut().unwrap().push(cause); + } + } + } + return Ok(None); + } + + let candidate_set = self.assemble_candidates(stack)?; + + if candidate_set.ambiguous { + debug!("candidate set contains ambig"); + return Ok(None); + } + + let mut candidates = candidate_set.vec; + + debug!("assembled {} candidates for {:?}: {:?}", candidates.len(), stack, candidates); + + // At this point, we know that each of the entries in the + // candidate set is *individually* applicable. Now we have to + // figure out if they contain mutual incompatibilities. This + // frequently arises if we have an unconstrained input type -- + // for example, we are looking for `$0: Eq` where `$0` is some + // unconstrained type variable. In that case, we'll get a + // candidate which assumes $0 == int, one that assumes `$0 == + // usize`, etc. This spells an ambiguity. + + // If there is more than one candidate, first winnow them down + // by considering extra conditions (nested obligations and so + // forth). We don't winnow if there is exactly one + // candidate. This is a relatively minor distinction but it + // can lead to better inference and error-reporting. An + // example would be if there was an impl: + // + // impl Vec { fn push_clone(...) { ... } } + // + // and we were to see some code `foo.push_clone()` where `boo` + // is a `Vec` and `Bar` does not implement `Clone`. If + // we were to winnow, we'd wind up with zero candidates. + // Instead, we select the right impl now but report "`Bar` does + // not implement `Clone`". + if candidates.len() == 1 { + return self.filter_negative_and_reservation_impls(candidates.pop().unwrap()); + } + + // Winnow, but record the exact outcome of evaluation, which + // is needed for specialization. Propagate overflow if it occurs. + let mut candidates = candidates + .into_iter() + .map(|c| match self.evaluate_candidate(stack, &c) { + Ok(eval) if eval.may_apply() => { + Ok(Some(EvaluatedCandidate { candidate: c, evaluation: eval })) + } + Ok(_) => Ok(None), + Err(OverflowError) => Err(Overflow), + }) + .flat_map(Result::transpose) + .collect::, _>>()?; + + debug!("winnowed to {} candidates for {:?}: {:?}", candidates.len(), stack, candidates); + + let needs_infer = stack.obligation.predicate.needs_infer(); + + // If there are STILL multiple candidates, we can further + // reduce the list by dropping duplicates -- including + // resolving specializations. + if candidates.len() > 1 { + let mut i = 0; + while i < candidates.len() { + let is_dup = (0..candidates.len()).filter(|&j| i != j).any(|j| { + self.candidate_should_be_dropped_in_favor_of( + &candidates[i], + &candidates[j], + needs_infer, + ) + }); + if is_dup { + debug!("Dropping candidate #{}/{}: {:?}", i, candidates.len(), candidates[i]); + candidates.swap_remove(i); + } else { + debug!("Retaining candidate #{}/{}: {:?}", i, candidates.len(), candidates[i]); + i += 1; + + // If there are *STILL* multiple candidates, give up + // and report ambiguity. + if i > 1 { + debug!("multiple matches, ambig"); + return Ok(None); + } + } + } + } + + // If there are *NO* candidates, then there are no impls -- + // that we know of, anyway. Note that in the case where there + // are unbound type variables within the obligation, it might + // be the case that you could still satisfy the obligation + // from another crate by instantiating the type variables with + // a type from another crate that does have an impl. This case + // is checked for in `evaluate_stack` (and hence users + // who might care about this case, like coherence, should use + // that function). + if candidates.is_empty() { + // If there's an error type, 'downgrade' our result from + // `Err(Unimplemented)` to `Ok(None)`. This helps us avoid + // emitting additional spurious errors, since we're guaranteed + // to have emitted at least one. + if stack.obligation.references_error() { + debug!("no results for error type, treating as ambiguous"); + return Ok(None); + } + return Err(Unimplemented); + } + + // Just one candidate left. + self.filter_negative_and_reservation_impls(candidates.pop().unwrap().candidate) + } + pub(super) fn assemble_candidates<'o>( &mut self, stack: &TraitObligationStack<'o, 'tcx>, diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 57f1fedacbe5a..114dc79c44f50 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1029,161 +1029,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ok(Some(candidate)) } - fn candidate_from_obligation_no_cache<'o>( - &mut self, - stack: &TraitObligationStack<'o, 'tcx>, - ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { - if let Some(conflict) = self.is_knowable(stack) { - debug!("coherence stage: not knowable"); - if self.intercrate_ambiguity_causes.is_some() { - debug!("evaluate_stack: intercrate_ambiguity_causes is some"); - // Heuristics: show the diagnostics when there are no candidates in crate. - if let Ok(candidate_set) = self.assemble_candidates(stack) { - let mut no_candidates_apply = true; - - for c in candidate_set.vec.iter() { - if self.evaluate_candidate(stack, &c)?.may_apply() { - no_candidates_apply = false; - break; - } - } - - if !candidate_set.ambiguous && no_candidates_apply { - let trait_ref = stack.obligation.predicate.skip_binder().trait_ref; - let self_ty = trait_ref.self_ty(); - let (trait_desc, self_desc) = with_no_trimmed_paths(|| { - let trait_desc = trait_ref.print_only_trait_path().to_string(); - let self_desc = if self_ty.has_concrete_skeleton() { - Some(self_ty.to_string()) - } else { - None - }; - (trait_desc, self_desc) - }); - let cause = if let Conflict::Upstream = conflict { - IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_desc, self_desc } - } else { - IntercrateAmbiguityCause::DownstreamCrate { trait_desc, self_desc } - }; - debug!("evaluate_stack: pushing cause = {:?}", cause); - self.intercrate_ambiguity_causes.as_mut().unwrap().push(cause); - } - } - } - return Ok(None); - } - - let candidate_set = self.assemble_candidates(stack)?; - - if candidate_set.ambiguous { - debug!("candidate set contains ambig"); - return Ok(None); - } - - let mut candidates = candidate_set.vec; - - debug!("assembled {} candidates for {:?}: {:?}", candidates.len(), stack, candidates); - - // At this point, we know that each of the entries in the - // candidate set is *individually* applicable. Now we have to - // figure out if they contain mutual incompatibilities. This - // frequently arises if we have an unconstrained input type -- - // for example, we are looking for `$0: Eq` where `$0` is some - // unconstrained type variable. In that case, we'll get a - // candidate which assumes $0 == int, one that assumes `$0 == - // usize`, etc. This spells an ambiguity. - - // If there is more than one candidate, first winnow them down - // by considering extra conditions (nested obligations and so - // forth). We don't winnow if there is exactly one - // candidate. This is a relatively minor distinction but it - // can lead to better inference and error-reporting. An - // example would be if there was an impl: - // - // impl Vec { fn push_clone(...) { ... } } - // - // and we were to see some code `foo.push_clone()` where `boo` - // is a `Vec` and `Bar` does not implement `Clone`. If - // we were to winnow, we'd wind up with zero candidates. - // Instead, we select the right impl now but report "`Bar` does - // not implement `Clone`". - if candidates.len() == 1 { - return self.filter_negative_and_reservation_impls(candidates.pop().unwrap()); - } - - // Winnow, but record the exact outcome of evaluation, which - // is needed for specialization. Propagate overflow if it occurs. - let mut candidates = candidates - .into_iter() - .map(|c| match self.evaluate_candidate(stack, &c) { - Ok(eval) if eval.may_apply() => { - Ok(Some(EvaluatedCandidate { candidate: c, evaluation: eval })) - } - Ok(_) => Ok(None), - Err(OverflowError) => Err(Overflow), - }) - .flat_map(Result::transpose) - .collect::, _>>()?; - - debug!("winnowed to {} candidates for {:?}: {:?}", candidates.len(), stack, candidates); - - let needs_infer = stack.obligation.predicate.needs_infer(); - - // If there are STILL multiple candidates, we can further - // reduce the list by dropping duplicates -- including - // resolving specializations. - if candidates.len() > 1 { - let mut i = 0; - while i < candidates.len() { - let is_dup = (0..candidates.len()).filter(|&j| i != j).any(|j| { - self.candidate_should_be_dropped_in_favor_of( - &candidates[i], - &candidates[j], - needs_infer, - ) - }); - if is_dup { - debug!("Dropping candidate #{}/{}: {:?}", i, candidates.len(), candidates[i]); - candidates.swap_remove(i); - } else { - debug!("Retaining candidate #{}/{}: {:?}", i, candidates.len(), candidates[i]); - i += 1; - - // If there are *STILL* multiple candidates, give up - // and report ambiguity. - if i > 1 { - debug!("multiple matches, ambig"); - return Ok(None); - } - } - } - } - - // If there are *NO* candidates, then there are no impls -- - // that we know of, anyway. Note that in the case where there - // are unbound type variables within the obligation, it might - // be the case that you could still satisfy the obligation - // from another crate by instantiating the type variables with - // a type from another crate that does have an impl. This case - // is checked for in `evaluate_stack` (and hence users - // who might care about this case, like coherence, should use - // that function). - if candidates.is_empty() { - // If there's an error type, 'downgrade' our result from - // `Err(Unimplemented)` to `Ok(None)`. This helps us avoid - // emitting additional spurious errors, since we're guaranteed - // to have emitted at least one. - if stack.obligation.references_error() { - debug!("no results for error type, treating as ambiguous"); - return Ok(None); - } - return Err(Unimplemented); - } - - // Just one candidate left. - self.filter_negative_and_reservation_impls(candidates.pop().unwrap().candidate) - } - fn is_knowable<'o>(&mut self, stack: &TraitObligationStack<'o, 'tcx>) -> Option { debug!("is_knowable(intercrate={:?})", self.intercrate); diff --git a/compiler/rustc_typeck/src/check/writeback.rs b/compiler/rustc_typeck/src/check/writeback.rs index 6fd7277a1c332..5363702a5be6d 100644 --- a/compiler/rustc_typeck/src/check/writeback.rs +++ b/compiler/rustc_typeck/src/check/writeback.rs @@ -70,10 +70,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("used_trait_imports({:?}) = {:?}", item_def_id, used_trait_imports); wbcx.typeck_results.used_trait_imports = used_trait_imports; - wbcx.typeck_results.closure_captures = mem::replace( - &mut self.typeck_results.borrow_mut().closure_captures, - Default::default(), - ); + wbcx.typeck_results.closure_captures = + mem::take(&mut self.typeck_results.borrow_mut().closure_captures); if self.is_tainted_by_errors() { // FIXME(eddyb) keep track of `ErrorReported` from where the error was emitted. diff --git a/library/alloc/src/alloc.rs b/library/alloc/src/alloc.rs index 8b8cdbf252555..75158eefcac28 100644 --- a/library/alloc/src/alloc.rs +++ b/library/alloc/src/alloc.rs @@ -145,13 +145,13 @@ pub unsafe fn alloc_zeroed(layout: Layout) -> *mut u8 { impl Global { #[inline] - fn alloc_impl(&self, layout: Layout, zeroed: bool) -> Result, AllocErr> { + fn alloc_impl(&self, layout: Layout, zeroed: bool) -> Result, AllocError> { match layout.size() { 0 => Ok(NonNull::slice_from_raw_parts(layout.dangling(), 0)), // SAFETY: `layout` is non-zero in size, size => unsafe { let raw_ptr = if zeroed { alloc_zeroed(layout) } else { alloc(layout) }; - let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?; + let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?; Ok(NonNull::slice_from_raw_parts(ptr, size)) }, } @@ -165,7 +165,7 @@ impl Global { old_layout: Layout, new_layout: Layout, zeroed: bool, - ) -> Result, AllocErr> { + ) -> Result, AllocError> { debug_assert!( new_layout.size() >= old_layout.size(), "`new_layout.size()` must be greater than or equal to `old_layout.size()`" @@ -183,7 +183,7 @@ impl Global { intrinsics::assume(new_size >= old_layout.size()); let raw_ptr = realloc(ptr.as_ptr(), old_layout, new_size); - let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?; + let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?; if zeroed { raw_ptr.add(old_size).write_bytes(0, new_size - old_size); } @@ -208,12 +208,12 @@ impl Global { #[unstable(feature = "allocator_api", issue = "32838")] unsafe impl AllocRef for Global { #[inline] - fn alloc(&self, layout: Layout) -> Result, AllocErr> { + fn alloc(&self, layout: Layout) -> Result, AllocError> { self.alloc_impl(layout, false) } #[inline] - fn alloc_zeroed(&self, layout: Layout) -> Result, AllocErr> { + fn alloc_zeroed(&self, layout: Layout) -> Result, AllocError> { self.alloc_impl(layout, true) } @@ -232,7 +232,7 @@ unsafe impl AllocRef for Global { ptr: NonNull, old_layout: Layout, new_layout: Layout, - ) -> Result, AllocErr> { + ) -> Result, AllocError> { // SAFETY: all conditions must be upheld by the caller unsafe { self.grow_impl(ptr, old_layout, new_layout, false) } } @@ -243,7 +243,7 @@ unsafe impl AllocRef for Global { ptr: NonNull, old_layout: Layout, new_layout: Layout, - ) -> Result, AllocErr> { + ) -> Result, AllocError> { // SAFETY: all conditions must be upheld by the caller unsafe { self.grow_impl(ptr, old_layout, new_layout, true) } } @@ -254,7 +254,7 @@ unsafe impl AllocRef for Global { ptr: NonNull, old_layout: Layout, new_layout: Layout, - ) -> Result, AllocErr> { + ) -> Result, AllocError> { debug_assert!( new_layout.size() <= old_layout.size(), "`new_layout.size()` must be smaller than or equal to `old_layout.size()`" @@ -273,7 +273,7 @@ unsafe impl AllocRef for Global { intrinsics::assume(new_size <= old_layout.size()); let raw_ptr = realloc(ptr.as_ptr(), old_layout, new_size); - let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?; + let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?; Ok(NonNull::slice_from_raw_parts(ptr, new_size)) }, diff --git a/library/alloc/src/collections/btree/node/tests.rs b/library/alloc/src/collections/btree/node/tests.rs index e2416974ddca3..54c3709821acd 100644 --- a/library/alloc/src/collections/btree/node/tests.rs +++ b/library/alloc/src/collections/btree/node/tests.rs @@ -23,3 +23,12 @@ fn test_splitpoint() { assert!(left_len + right_len == CAPACITY); } } + +#[test] +#[cfg(target_arch = "x86_64")] +fn test_sizes() { + assert_eq!(core::mem::size_of::>(), 16); + assert_eq!(core::mem::size_of::>(), 16 + CAPACITY * 8 * 2); + assert_eq!(core::mem::size_of::>(), 112); + assert_eq!(core::mem::size_of::>(), 112 + CAPACITY * 8 * 2); +} diff --git a/library/alloc/src/raw_vec/tests.rs b/library/alloc/src/raw_vec/tests.rs index e4c8b3709dfee..cb4fe1b46cd75 100644 --- a/library/alloc/src/raw_vec/tests.rs +++ b/library/alloc/src/raw_vec/tests.rs @@ -3,7 +3,7 @@ use std::cell::Cell; #[test] fn allocator_param() { - use crate::alloc::AllocErr; + use crate::alloc::AllocError; // Writing a test of integration between third-party // allocators and `RawVec` is a little tricky because the `RawVec` @@ -21,10 +21,10 @@ fn allocator_param() { fuel: Cell, } unsafe impl AllocRef for BoundedAlloc { - fn alloc(&self, layout: Layout) -> Result, AllocErr> { + fn alloc(&self, layout: Layout) -> Result, AllocError> { let size = layout.size(); if size > self.fuel.get() { - return Err(AllocErr); + return Err(AllocError); } match Global.alloc(layout) { ok @ Ok(_) => { diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index f998e49dcfcde..5dbc42cc97d5b 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -247,7 +247,7 @@ use core::pin::Pin; use core::ptr::{self, NonNull}; use core::slice::from_raw_parts_mut; -use crate::alloc::{box_free, handle_alloc_error, AllocErr, AllocRef, Global, Layout}; +use crate::alloc::{box_free, handle_alloc_error, AllocError, AllocRef, Global, Layout}; use crate::borrow::{Cow, ToOwned}; use crate::string::String; use crate::vec::Vec; @@ -996,7 +996,7 @@ impl Rc { /// and must return back a (potentially fat)-pointer for the `RcBox`. unsafe fn allocate_for_layout( value_layout: Layout, - allocate: impl FnOnce(Layout) -> Result, AllocErr>, + allocate: impl FnOnce(Layout) -> Result, AllocError>, mem_to_rcbox: impl FnOnce(*mut u8) -> *mut RcBox, ) -> *mut RcBox { // Calculate layout using the given value layout. diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 6a240fbb42a99..3d7411c79dc4f 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -21,7 +21,7 @@ use core::slice::from_raw_parts_mut; use core::sync::atomic; use core::sync::atomic::Ordering::{Acquire, Relaxed, Release, SeqCst}; -use crate::alloc::{box_free, handle_alloc_error, AllocErr, AllocRef, Global, Layout}; +use crate::alloc::{box_free, handle_alloc_error, AllocError, AllocRef, Global, Layout}; use crate::borrow::{Cow, ToOwned}; use crate::boxed::Box; use crate::rc::is_dangling; @@ -969,7 +969,7 @@ impl Arc { /// and must return back a (potentially fat)-pointer for the `ArcInner`. unsafe fn allocate_for_layout( value_layout: Layout, - allocate: impl FnOnce(Layout) -> Result, AllocErr>, + allocate: impl FnOnce(Layout) -> Result, AllocError>, mem_to_arcinner: impl FnOnce(*mut u8) -> *mut ArcInner, ) -> *mut ArcInner { // Calculate layout using the given value layout. diff --git a/library/core/src/alloc/mod.rs b/library/core/src/alloc/mod.rs index 6b14e06e4527d..6d09b4f02635b 100644 --- a/library/core/src/alloc/mod.rs +++ b/library/core/src/alloc/mod.rs @@ -13,17 +13,17 @@ pub use self::layout::{Layout, LayoutErr}; use crate::fmt; use crate::ptr::{self, NonNull}; -/// The `AllocErr` error indicates an allocation failure +/// The `AllocError` error indicates an allocation failure /// that may be due to resource exhaustion or to /// something wrong when combining the given input arguments with this /// allocator. #[unstable(feature = "allocator_api", issue = "32838")] #[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub struct AllocErr; +pub struct AllocError; // (we need this for downstream impl of trait Error) #[unstable(feature = "allocator_api", issue = "32838")] -impl fmt::Display for AllocErr { +impl fmt::Display for AllocError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("memory allocation failed") } @@ -109,7 +109,7 @@ pub unsafe trait AllocRef { /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar. /// /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html - fn alloc(&self, layout: Layout) -> Result, AllocErr>; + fn alloc(&self, layout: Layout) -> Result, AllocError>; /// Behaves like `alloc`, but also ensures that the returned memory is zero-initialized. /// @@ -126,7 +126,7 @@ pub unsafe trait AllocRef { /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar. /// /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html - fn alloc_zeroed(&self, layout: Layout) -> Result, AllocErr> { + fn alloc_zeroed(&self, layout: Layout) -> Result, AllocError> { let ptr = self.alloc(layout)?; // SAFETY: `alloc` returns a valid memory block unsafe { ptr.as_non_null_ptr().as_ptr().write_bytes(0, ptr.len()) } @@ -187,7 +187,7 @@ pub unsafe trait AllocRef { ptr: NonNull, old_layout: Layout, new_layout: Layout, - ) -> Result, AllocErr> { + ) -> Result, AllocError> { debug_assert!( new_layout.size() >= old_layout.size(), "`new_layout.size()` must be greater than or equal to `old_layout.size()`" @@ -248,7 +248,7 @@ pub unsafe trait AllocRef { ptr: NonNull, old_layout: Layout, new_layout: Layout, - ) -> Result, AllocErr> { + ) -> Result, AllocError> { debug_assert!( new_layout.size() >= old_layout.size(), "`new_layout.size()` must be greater than or equal to `old_layout.size()`" @@ -312,7 +312,7 @@ pub unsafe trait AllocRef { ptr: NonNull, old_layout: Layout, new_layout: Layout, - ) -> Result, AllocErr> { + ) -> Result, AllocError> { debug_assert!( new_layout.size() <= old_layout.size(), "`new_layout.size()` must be smaller than or equal to `old_layout.size()`" @@ -348,12 +348,12 @@ where A: AllocRef + ?Sized, { #[inline] - fn alloc(&self, layout: Layout) -> Result, AllocErr> { + fn alloc(&self, layout: Layout) -> Result, AllocError> { (**self).alloc(layout) } #[inline] - fn alloc_zeroed(&self, layout: Layout) -> Result, AllocErr> { + fn alloc_zeroed(&self, layout: Layout) -> Result, AllocError> { (**self).alloc_zeroed(layout) } @@ -369,7 +369,7 @@ where ptr: NonNull, old_layout: Layout, new_layout: Layout, - ) -> Result, AllocErr> { + ) -> Result, AllocError> { // SAFETY: the safety contract must be upheld by the caller unsafe { (**self).grow(ptr, old_layout, new_layout) } } @@ -380,7 +380,7 @@ where ptr: NonNull, old_layout: Layout, new_layout: Layout, - ) -> Result, AllocErr> { + ) -> Result, AllocError> { // SAFETY: the safety contract must be upheld by the caller unsafe { (**self).grow_zeroed(ptr, old_layout, new_layout) } } @@ -391,7 +391,7 @@ where ptr: NonNull, old_layout: Layout, new_layout: Layout, - ) -> Result, AllocErr> { + ) -> Result, AllocError> { // SAFETY: the safety contract must be upheld by the caller unsafe { (**self).shrink(ptr, old_layout, new_layout) } } diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index 87a59c873b197..5dc7171a7dc26 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -448,7 +448,7 @@ impl NonNull<[T]> { /// // Note that calling `memory.as_mut()` is not allowed here as the content may be uninitialized. /// # #[allow(unused_variables)] /// let slice: &mut [MaybeUninit] = unsafe { memory.as_uninit_slice_mut() }; - /// # Ok::<_, std::alloc::AllocErr>(()) + /// # Ok::<_, std::alloc::AllocError>(()) /// ``` #[inline] #[unstable(feature = "ptr_as_uninit", issue = "75402")] diff --git a/library/std/src/alloc.rs b/library/std/src/alloc.rs index ba158511f64c0..dd7600623801f 100644 --- a/library/std/src/alloc.rs +++ b/library/std/src/alloc.rs @@ -133,7 +133,7 @@ pub struct System; impl System { #[inline] - fn alloc_impl(&self, layout: Layout, zeroed: bool) -> Result, AllocErr> { + fn alloc_impl(&self, layout: Layout, zeroed: bool) -> Result, AllocError> { match layout.size() { 0 => Ok(NonNull::slice_from_raw_parts(layout.dangling(), 0)), // SAFETY: `layout` is non-zero in size, @@ -143,7 +143,7 @@ impl System { } else { GlobalAlloc::alloc(self, layout) }; - let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?; + let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?; Ok(NonNull::slice_from_raw_parts(ptr, size)) }, } @@ -157,7 +157,7 @@ impl System { old_layout: Layout, new_layout: Layout, zeroed: bool, - ) -> Result, AllocErr> { + ) -> Result, AllocError> { debug_assert!( new_layout.size() >= old_layout.size(), "`new_layout.size()` must be greater than or equal to `old_layout.size()`" @@ -175,7 +175,7 @@ impl System { intrinsics::assume(new_size >= old_layout.size()); let raw_ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), old_layout, new_size); - let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?; + let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?; if zeroed { raw_ptr.add(old_size).write_bytes(0, new_size - old_size); } @@ -202,12 +202,12 @@ impl System { #[unstable(feature = "allocator_api", issue = "32838")] unsafe impl AllocRef for System { #[inline] - fn alloc(&self, layout: Layout) -> Result, AllocErr> { + fn alloc(&self, layout: Layout) -> Result, AllocError> { self.alloc_impl(layout, false) } #[inline] - fn alloc_zeroed(&self, layout: Layout) -> Result, AllocErr> { + fn alloc_zeroed(&self, layout: Layout) -> Result, AllocError> { self.alloc_impl(layout, true) } @@ -226,7 +226,7 @@ unsafe impl AllocRef for System { ptr: NonNull, old_layout: Layout, new_layout: Layout, - ) -> Result, AllocErr> { + ) -> Result, AllocError> { // SAFETY: all conditions must be upheld by the caller unsafe { self.grow_impl(ptr, old_layout, new_layout, false) } } @@ -237,7 +237,7 @@ unsafe impl AllocRef for System { ptr: NonNull, old_layout: Layout, new_layout: Layout, - ) -> Result, AllocErr> { + ) -> Result, AllocError> { // SAFETY: all conditions must be upheld by the caller unsafe { self.grow_impl(ptr, old_layout, new_layout, true) } } @@ -248,7 +248,7 @@ unsafe impl AllocRef for System { ptr: NonNull, old_layout: Layout, new_layout: Layout, - ) -> Result, AllocErr> { + ) -> Result, AllocError> { debug_assert!( new_layout.size() <= old_layout.size(), "`new_layout.size()` must be smaller than or equal to `old_layout.size()`" @@ -267,7 +267,7 @@ unsafe impl AllocRef for System { intrinsics::assume(new_size <= old_layout.size()); let raw_ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), old_layout, new_size); - let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?; + let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?; Ok(NonNull::slice_from_raw_parts(ptr, new_size)) }, diff --git a/library/std/src/error.rs b/library/std/src/error.rs index ee25311d3b7ee..5771ca758afb0 100644 --- a/library/std/src/error.rs +++ b/library/std/src/error.rs @@ -19,7 +19,7 @@ mod tests; use core::array; use core::convert::Infallible; -use crate::alloc::{AllocErr, LayoutErr}; +use crate::alloc::{AllocError, LayoutErr}; use crate::any::TypeId; use crate::backtrace::Backtrace; use crate::borrow::Cow; @@ -387,7 +387,7 @@ impl Error for ! {} reason = "the precise API and guarantees it provides may be tweaked.", issue = "32838" )] -impl Error for AllocErr {} +impl Error for AllocError {} #[stable(feature = "alloc_layout", since = "1.28.0")] impl Error for LayoutErr {} diff --git a/src/ci/run.sh b/src/ci/run.sh index 5231aa2e76619..181a7fcb73266 100755 --- a/src/ci/run.sh +++ b/src/ci/run.sh @@ -104,6 +104,8 @@ if [ "$RUST_RELEASE_CHANNEL" = "nightly" ] || [ "$DIST_REQUIRE_ALL_TOOLS" = "" ] RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-missing-tools" fi +export COMPILETEST_NEEDS_ALL_LLVM_COMPONENTS=1 + # Print the date from the local machine and the date from an external source to # check for clock drifts. An HTTP URL is used instead of HTTPS since on Azure # Pipelines it happened that the certificates were marked as expired. diff --git a/src/test/ui/const-generics/const_evaluatable_checked/cross_crate_predicate.rs b/src/test/ui/const-generics/const_evaluatable_checked/cross_crate_predicate.rs index 52b89cfa04588..e3a4d9a96aa1e 100644 --- a/src/test/ui/const-generics/const_evaluatable_checked/cross_crate_predicate.rs +++ b/src/test/ui/const-generics/const_evaluatable_checked/cross_crate_predicate.rs @@ -5,10 +5,10 @@ extern crate const_evaluatable_lib; fn user() { let _ = const_evaluatable_lib::test1::(); - //~^ ERROR constant expression depends - //~| ERROR constant expression depends - //~| ERROR constant expression depends - //~| ERROR constant expression depends + //~^ ERROR unconstrained generic constant + //~| ERROR unconstrained generic constant + //~| ERROR unconstrained generic constant + //~| ERROR unconstrained generic constant } fn main() {} diff --git a/src/test/ui/const-generics/const_evaluatable_checked/cross_crate_predicate.stderr b/src/test/ui/const-generics/const_evaluatable_checked/cross_crate_predicate.stderr index 4af68118be31f..8a298b47fffa7 100644 --- a/src/test/ui/const-generics/const_evaluatable_checked/cross_crate_predicate.stderr +++ b/src/test/ui/const-generics/const_evaluatable_checked/cross_crate_predicate.stderr @@ -1,54 +1,50 @@ -error: constant expression depends on a generic parameter +error: unconstrained generic constant --> $DIR/cross_crate_predicate.rs:7:13 | LL | let _ = const_evaluatable_lib::test1::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - ::: $DIR/auxiliary/const_evaluatable_lib.rs:6:10 | -LL | [u8; std::mem::size_of::() - 1]: Sized, - | ---------------------------- required by this bound in `test1` +help: consider adding a `where` bound for this expression + --> $DIR/auxiliary/const_evaluatable_lib.rs:6:10 | - = note: this may fail depending on what value the parameter takes +LL | [u8; std::mem::size_of::() - 1]: Sized, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: constant expression depends on a generic parameter +error: unconstrained generic constant --> $DIR/cross_crate_predicate.rs:7:13 | LL | let _ = const_evaluatable_lib::test1::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - ::: $DIR/auxiliary/const_evaluatable_lib.rs:4:27 | -LL | pub fn test1() -> [u8; std::mem::size_of::() - 1] - | ---------------------------- required by this bound in `test1` +help: consider adding a `where` bound for this expression + --> $DIR/auxiliary/const_evaluatable_lib.rs:4:27 | - = note: this may fail depending on what value the parameter takes +LL | pub fn test1() -> [u8; std::mem::size_of::() - 1] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: constant expression depends on a generic parameter +error: unconstrained generic constant --> $DIR/cross_crate_predicate.rs:7:13 | LL | let _ = const_evaluatable_lib::test1::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - ::: $DIR/auxiliary/const_evaluatable_lib.rs:6:10 | -LL | [u8; std::mem::size_of::() - 1]: Sized, - | ---------------------------- required by this bound in `test1` +help: consider adding a `where` bound for this expression + --> $DIR/auxiliary/const_evaluatable_lib.rs:6:10 | - = note: this may fail depending on what value the parameter takes +LL | [u8; std::mem::size_of::() - 1]: Sized, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: constant expression depends on a generic parameter +error: unconstrained generic constant --> $DIR/cross_crate_predicate.rs:7:13 | LL | let _ = const_evaluatable_lib::test1::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - ::: $DIR/auxiliary/const_evaluatable_lib.rs:4:27 | -LL | pub fn test1() -> [u8; std::mem::size_of::() - 1] - | ---------------------------- required by this bound in `test1` +help: consider adding a `where` bound for this expression + --> $DIR/auxiliary/const_evaluatable_lib.rs:4:27 | - = note: this may fail depending on what value the parameter takes +LL | pub fn test1() -> [u8; std::mem::size_of::() - 1] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 4 previous errors diff --git a/src/test/ui/const-generics/const_evaluatable_checked/infer-too-generic.rs b/src/test/ui/const-generics/const_evaluatable_checked/infer-too-generic.rs new file mode 100644 index 0000000000000..cad06ea4004af --- /dev/null +++ b/src/test/ui/const-generics/const_evaluatable_checked/infer-too-generic.rs @@ -0,0 +1,24 @@ +// run-pass +#![feature(const_generics, const_evaluatable_checked)] +#![allow(incomplete_features)] + +use std::{mem, ptr}; + +fn split_first(arr: [T; N]) -> (T, [T; N - 1]) +where + [T; N - 1]: Sized, +{ + let arr = mem::ManuallyDrop::new(arr); + unsafe { + let head = ptr::read(&arr[0]); + let tail = ptr::read(&arr[1..] as *const [T] as *const [T; N - 1]); + (head, tail) + } +} + +fn main() { + let arr = [0, 1, 2, 3, 4]; + let (head, tail) = split_first(arr); + assert_eq!(head, 0); + assert_eq!(tail, [1, 2, 3, 4]); +} diff --git a/src/test/ui/const-generics/issues/issue-76595.rs b/src/test/ui/const-generics/issues/issue-76595.rs index 0a16ca181f557..9fdbbff66e9e9 100644 --- a/src/test/ui/const-generics/issues/issue-76595.rs +++ b/src/test/ui/const-generics/issues/issue-76595.rs @@ -14,5 +14,4 @@ fn test() where Bool<{core::mem::size_of::() > 4}>: True { fn main() { test::<2>(); //~^ ERROR wrong number of type - //~| ERROR constant expression depends } diff --git a/src/test/ui/const-generics/issues/issue-76595.stderr b/src/test/ui/const-generics/issues/issue-76595.stderr index bbc81693fc0ef..f258d2977186f 100644 --- a/src/test/ui/const-generics/issues/issue-76595.stderr +++ b/src/test/ui/const-generics/issues/issue-76595.stderr @@ -4,17 +4,6 @@ error[E0107]: wrong number of type arguments: expected 1, found 0 LL | test::<2>(); | ^^^^^^^^^ expected 1 type argument -error: constant expression depends on a generic parameter - --> $DIR/issue-76595.rs:15:5 - | -LL | fn test() where Bool<{core::mem::size_of::() > 4}>: True { - | ------------------------------- required by this bound in `test` -... -LL | test::<2>(); - | ^^^^^^^^^ - | - = note: this may fail depending on what value the parameter takes - -error: aborting due to 2 previous errors +error: aborting due to previous error For more information about this error, try `rustc --explain E0107`. diff --git a/src/test/ui/invalid-rustc_args_required_const-arguments.rs b/src/test/ui/invalid-rustc_args_required_const-arguments.rs new file mode 100644 index 0000000000000..76c01c2130138 --- /dev/null +++ b/src/test/ui/invalid-rustc_args_required_const-arguments.rs @@ -0,0 +1,26 @@ +#![feature(rustc_attrs)] + +#[rustc_args_required_const(0)] //~ ERROR index exceeds number of arguments +fn foo1() {} + +#[rustc_args_required_const(1)] //~ ERROR index exceeds number of arguments +fn foo2(_: u8) {} + +#[rustc_args_required_const(a)] //~ ERROR arguments should be non-negative integers +fn foo4() {} + +#[rustc_args_required_const(1, a, 2, b)] //~ ERROR arguments should be non-negative integers +fn foo5(_: u8, _: u8, _: u8) {} + +#[rustc_args_required_const(0)] //~ ERROR attribute should be applied to a function +struct S; + +#[rustc_args_required_const(0usize)] //~ ERROR suffixed literals are not allowed in attributes +fn foo6(_: u8) {} + +extern { + #[rustc_args_required_const(1)] //~ ERROR index exceeds number of arguments + fn foo7(_: u8); +} + +fn main() {} diff --git a/src/test/ui/invalid-rustc_args_required_const-arguments.stderr b/src/test/ui/invalid-rustc_args_required_const-arguments.stderr new file mode 100644 index 0000000000000..39d0462616873 --- /dev/null +++ b/src/test/ui/invalid-rustc_args_required_const-arguments.stderr @@ -0,0 +1,48 @@ +error: suffixed literals are not allowed in attributes + --> $DIR/invalid-rustc_args_required_const-arguments.rs:18:29 + | +LL | #[rustc_args_required_const(0usize)] + | ^^^^^^ + | + = help: instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.) + +error: index exceeds number of arguments + --> $DIR/invalid-rustc_args_required_const-arguments.rs:3:29 + | +LL | #[rustc_args_required_const(0)] + | ^ there are only 0 arguments + +error: index exceeds number of arguments + --> $DIR/invalid-rustc_args_required_const-arguments.rs:6:29 + | +LL | #[rustc_args_required_const(1)] + | ^ there is only 1 argument + +error: arguments should be non-negative integers + --> $DIR/invalid-rustc_args_required_const-arguments.rs:9:29 + | +LL | #[rustc_args_required_const(a)] + | ^ + +error: arguments should be non-negative integers + --> $DIR/invalid-rustc_args_required_const-arguments.rs:12:32 + | +LL | #[rustc_args_required_const(1, a, 2, b)] + | ^ ^ + +error: attribute should be applied to a function + --> $DIR/invalid-rustc_args_required_const-arguments.rs:15:1 + | +LL | #[rustc_args_required_const(0)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | struct S; + | --------- not a function + +error: index exceeds number of arguments + --> $DIR/invalid-rustc_args_required_const-arguments.rs:22:33 + | +LL | #[rustc_args_required_const(1)] + | ^ there is only 1 argument + +error: aborting due to 7 previous errors + diff --git a/src/test/ui/issues/issue-75777.rs b/src/test/ui/issues/issue-75777.rs new file mode 100644 index 0000000000000..291a3db093672 --- /dev/null +++ b/src/test/ui/issues/issue-75777.rs @@ -0,0 +1,17 @@ +// Regression test for #75777. +// Checks that a boxed future can be properly constructed. + +#![feature(future_readiness_fns)] + +use std::future::{self, Future}; +use std::pin::Pin; + +type BoxFuture<'a, T> = Pin + 'a + Send>>; + +fn inject<'a, Env: 'a, A: 'a + Send>(v: A) -> Box BoxFuture<'a, A>> { + let fut: BoxFuture<'a, A> = Box::pin(future::ready(v)); + Box::new(move |_| fut) + //~^ ERROR: cannot infer an appropriate lifetime +} + +fn main() {} diff --git a/src/test/ui/issues/issue-75777.stderr b/src/test/ui/issues/issue-75777.stderr new file mode 100644 index 0000000000000..1115f6c6d52ca --- /dev/null +++ b/src/test/ui/issues/issue-75777.stderr @@ -0,0 +1,30 @@ +error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements + --> $DIR/issue-75777.rs:13:14 + | +LL | Box::new(move |_| fut) + | ^^^^^^^^^^^^ + | +note: first, the lifetime cannot outlive the lifetime `'a` as defined on the function body at 11:11... + --> $DIR/issue-75777.rs:11:11 + | +LL | fn inject<'a, Env: 'a, A: 'a + Send>(v: A) -> Box BoxFuture<'a, A>> { + | ^^ +note: ...so that the types are compatible + --> $DIR/issue-75777.rs:13:14 + | +LL | Box::new(move |_| fut) + | ^^^^^^^^^^^^ + = note: expected `Pin + Send>>` + found `Pin + Send + 'a)>>` + = note: but, the lifetime must be valid for the static lifetime... +note: ...so that the expression is assignable + --> $DIR/issue-75777.rs:13:5 + | +LL | Box::new(move |_| fut) + | ^^^^^^^^^^^^^^^^^^^^^^ + = note: expected `Box<(dyn FnOnce(&'a Env) -> Pin + Send + 'a)>> + 'static)>` + found `Box Pin + Send + 'a)>>>` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0495`. diff --git a/src/test/ui/lint/lint-const-item-mutation.rs b/src/test/ui/lint/lint-const-item-mutation.rs index 92d29a7dae475..43371560e02c1 100644 --- a/src/test/ui/lint/lint-const-item-mutation.rs +++ b/src/test/ui/lint/lint-const-item-mutation.rs @@ -3,13 +3,15 @@ struct MyStruct { field: bool, inner_array: [char; 1], + raw_ptr: *mut u8 } impl MyStruct { fn use_mut(&mut self) {} } const ARRAY: [u8; 1] = [25]; -const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'] }; +const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 }; +const RAW_PTR: *mut u8 = 1 as *mut u8; fn main() { ARRAY[0] = 5; //~ WARN attempting to modify @@ -18,4 +20,13 @@ fn main() { MY_STRUCT.use_mut(); //~ WARN taking &mut MY_STRUCT; //~ WARN taking (&mut MY_STRUCT).use_mut(); //~ WARN taking + + // Test that we don't warn when writing through + // a raw pointer + // This is U.B., but this test is check-pass, + // so this never actually executes + unsafe { + *RAW_PTR = 0; + *MY_STRUCT.raw_ptr = 0; + } } diff --git a/src/test/ui/lint/lint-const-item-mutation.stderr b/src/test/ui/lint/lint-const-item-mutation.stderr index 2d8f2c49744ba..c5a221128ffab 100644 --- a/src/test/ui/lint/lint-const-item-mutation.stderr +++ b/src/test/ui/lint/lint-const-item-mutation.stderr @@ -1,5 +1,5 @@ warning: attempting to modify a `const` item - --> $DIR/lint-const-item-mutation.rs:15:5 + --> $DIR/lint-const-item-mutation.rs:17:5 | LL | ARRAY[0] = 5; | ^^^^^^^^^^^^ @@ -7,39 +7,39 @@ LL | ARRAY[0] = 5; = note: `#[warn(const_item_mutation)]` on by default = note: each usage of a `const` item creates a new temporary - the original `const` item will not be modified note: `const` item defined here - --> $DIR/lint-const-item-mutation.rs:11:1 + --> $DIR/lint-const-item-mutation.rs:12:1 | LL | const ARRAY: [u8; 1] = [25]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: attempting to modify a `const` item - --> $DIR/lint-const-item-mutation.rs:16:5 + --> $DIR/lint-const-item-mutation.rs:18:5 | LL | MY_STRUCT.field = false; | ^^^^^^^^^^^^^^^^^^^^^^^ | = note: each usage of a `const` item creates a new temporary - the original `const` item will not be modified note: `const` item defined here - --> $DIR/lint-const-item-mutation.rs:12:1 + --> $DIR/lint-const-item-mutation.rs:13:1 | -LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'] }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: attempting to modify a `const` item - --> $DIR/lint-const-item-mutation.rs:17:5 + --> $DIR/lint-const-item-mutation.rs:19:5 | LL | MY_STRUCT.inner_array[0] = 'b'; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: each usage of a `const` item creates a new temporary - the original `const` item will not be modified note: `const` item defined here - --> $DIR/lint-const-item-mutation.rs:12:1 + --> $DIR/lint-const-item-mutation.rs:13:1 | -LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'] }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: taking a mutable reference to a `const` item - --> $DIR/lint-const-item-mutation.rs:18:5 + --> $DIR/lint-const-item-mutation.rs:20:5 | LL | MY_STRUCT.use_mut(); | ^^^^^^^^^^^^^^^^^^^ @@ -47,18 +47,18 @@ LL | MY_STRUCT.use_mut(); = note: each usage of a `const` item creates a new temporary = note: the mutable reference will refer to this temporary, not the original `const` item note: mutable reference created due to call to this method - --> $DIR/lint-const-item-mutation.rs:8:5 + --> $DIR/lint-const-item-mutation.rs:9:5 | LL | fn use_mut(&mut self) {} | ^^^^^^^^^^^^^^^^^^^^^ note: `const` item defined here - --> $DIR/lint-const-item-mutation.rs:12:1 + --> $DIR/lint-const-item-mutation.rs:13:1 | -LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'] }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: taking a mutable reference to a `const` item - --> $DIR/lint-const-item-mutation.rs:19:5 + --> $DIR/lint-const-item-mutation.rs:21:5 | LL | &mut MY_STRUCT; | ^^^^^^^^^^^^^^ @@ -66,13 +66,13 @@ LL | &mut MY_STRUCT; = note: each usage of a `const` item creates a new temporary = note: the mutable reference will refer to this temporary, not the original `const` item note: `const` item defined here - --> $DIR/lint-const-item-mutation.rs:12:1 + --> $DIR/lint-const-item-mutation.rs:13:1 | -LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'] }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: taking a mutable reference to a `const` item - --> $DIR/lint-const-item-mutation.rs:20:5 + --> $DIR/lint-const-item-mutation.rs:22:5 | LL | (&mut MY_STRUCT).use_mut(); | ^^^^^^^^^^^^^^^^ @@ -80,10 +80,10 @@ LL | (&mut MY_STRUCT).use_mut(); = note: each usage of a `const` item creates a new temporary = note: the mutable reference will refer to this temporary, not the original `const` item note: `const` item defined here - --> $DIR/lint-const-item-mutation.rs:12:1 + --> $DIR/lint-const-item-mutation.rs:13:1 | -LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'] }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: 6 warnings emitted diff --git a/src/tools/cargo b/src/tools/cargo index 05c611ae3c425..75615f8e69f74 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 05c611ae3c4255b7a2bcf4fcfa65b20286a07839 +Subproject commit 75615f8e69f748d7ef0df7bc0b064a9b1f5c78b2 diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index 17649dfab3750..59f64e7df0f41 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -208,10 +208,13 @@ impl EarlyProps { config.parse_name_value_directive(line, "needs-llvm-components") { let components: HashSet<_> = config.llvm_components.split_whitespace().collect(); - if !needed_components + if let Some(missing_component) = needed_components .split_whitespace() - .all(|needed_component| components.contains(needed_component)) + .find(|needed_component| !components.contains(needed_component)) { + if env::var_os("COMPILETEST_NEEDS_ALL_LLVM_COMPONENTS").is_some() { + panic!("missing LLVM component: {}", missing_component); + } return true; } }