diff --git a/Cargo.lock b/Cargo.lock index 65d20190c0db5..d0aaa995da973 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1707,9 +1707,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.79" +version = "0.2.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743" +checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614" dependencies = [ "rustc-std-workspace-core", ] diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs index 1a85a46ed74f0..a852254766699 100644 --- a/compiler/rustc_arena/src/lib.rs +++ b/compiler/rustc_arena/src/lib.rs @@ -533,7 +533,7 @@ impl DropArena { ptr::write(mem, object); let result = &mut *mem; // Record the destructor after doing the allocation as that may panic - // and would cause `object`'s destuctor to run twice if it was recorded before + // and would cause `object`'s destructor to run twice if it was recorded before self.destructors .borrow_mut() .push(DropType { drop_fn: drop_for_type::, obj: result as *mut T as *mut u8 }); @@ -560,7 +560,7 @@ impl DropArena { mem::forget(vec.drain(..)); // Record the destructors after doing the allocation as that may panic - // and would cause `object`'s destuctor to run twice if it was recorded before + // and would cause `object`'s destructor to run twice if it was recorded before for i in 0..len { destructors.push(DropType { drop_fn: drop_for_type::, diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index ea1a7cfa5d3b5..3902df8a7cac3 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -128,7 +128,8 @@ pub fn target_machine_factory( let (opt_level, _) = to_llvm_opt_settings(optlvl); let use_softfp = sess.opts.cg.soft_float; - let ffunction_sections = sess.target.options.function_sections; + let ffunction_sections = + sess.opts.debugging_opts.function_sections.unwrap_or(sess.target.options.function_sections); let fdata_sections = ffunction_sections; let code_model = to_llvm_code_model(sess.code_model()); diff --git a/compiler/rustc_data_structures/src/graph/vec_graph/mod.rs b/compiler/rustc_data_structures/src/graph/vec_graph/mod.rs index 064467174cae2..4ed8887841814 100644 --- a/compiler/rustc_data_structures/src/graph/vec_graph/mod.rs +++ b/compiler/rustc_data_structures/src/graph/vec_graph/mod.rs @@ -29,7 +29,7 @@ impl VecGraph { // Create the *edge starts* array. We are iterating over over // the (sorted) edge pairs. We maintain the invariant that the - // length of the `node_starts` arary is enough to store the + // length of the `node_starts` array is enough to store the // current source node -- so when we see that the source node // for an edge is greater than the current length, we grow the // edge-starts array by just enough. diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index bbb47a6e8071e..9dbd59506b188 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -63,6 +63,13 @@ impl mut_visit::MutVisitor for TokenStripper { i.tokens = None; mut_visit::noop_flat_map_item(i, self) } + fn flat_map_foreign_item( + &mut self, + mut i: P, + ) -> SmallVec<[P; 1]> { + i.tokens = None; + mut_visit::noop_flat_map_foreign_item(i, self) + } fn visit_block(&mut self, b: &mut P) { b.tokens = None; mut_visit::noop_visit_block(b, self); diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 6553d0ecfdb5f..235e049c3f566 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -550,6 +550,7 @@ fn test_debugging_options_tracking_hash() { tracked!(force_overflow_checks, Some(true)); tracked!(force_unstable_if_unmarked, true); tracked!(fuel, Some(("abc".to_string(), 99))); + tracked!(function_sections, Some(false)); tracked!(human_readable_cgu_names, true); tracked!(inline_in_all_cgus, Some(true)); tracked!(insert_sideeffect, true); diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 1db59bfc39dce..c8990842d32e6 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -49,6 +49,7 @@ mod early; mod internal; mod late; mod levels; +mod methods; mod non_ascii_idents; mod nonstandard_style; mod passes; @@ -73,6 +74,7 @@ use rustc_span::Span; use array_into_iter::ArrayIntoIter; use builtin::*; use internal::*; +use methods::*; use non_ascii_idents::*; use nonstandard_style::*; use redundant_semicolon::*; @@ -160,6 +162,7 @@ macro_rules! late_lint_passes { ArrayIntoIter: ArrayIntoIter, ClashingExternDeclarations: ClashingExternDeclarations::new(), DropTraitConstraints: DropTraitConstraints, + TemporaryCStringAsPtr: TemporaryCStringAsPtr, ] ); }; diff --git a/compiler/rustc_lint/src/methods.rs b/compiler/rustc_lint/src/methods.rs new file mode 100644 index 0000000000000..8732845af0cec --- /dev/null +++ b/compiler/rustc_lint/src/methods.rs @@ -0,0 +1,106 @@ +use crate::LateContext; +use crate::LateLintPass; +use crate::LintContext; +use rustc_hir::{Expr, ExprKind, PathSegment}; +use rustc_middle::ty; +use rustc_span::{symbol::sym, ExpnKind, Span}; + +declare_lint! { + /// The `temporary_cstring_as_ptr` lint detects getting the inner pointer of + /// a temporary `CString`. + /// + /// ### Example + /// + /// ```rust + /// # #![allow(unused)] + /// # use std::ffi::CString; + /// let c_str = CString::new("foo").unwrap().as_ptr(); + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The inner pointer of a `CString` lives only as long as the `CString` it + /// points to. Getting the inner pointer of a *temporary* `CString` allows the `CString` + /// to be dropped at the end of the statement, as it is not being referenced as far as the typesystem + /// is concerned. This means outside of the statement the pointer will point to freed memory, which + /// causes undefined behavior if the pointer is later dereferenced. + pub TEMPORARY_CSTRING_AS_PTR, + Warn, + "detects getting the inner pointer of a temporary `CString`" +} + +declare_lint_pass!(TemporaryCStringAsPtr => [TEMPORARY_CSTRING_AS_PTR]); + +fn in_macro(span: Span) -> bool { + if span.from_expansion() { + !matches!(span.ctxt().outer_expn_data().kind, ExpnKind::Desugaring(..)) + } else { + false + } +} + +fn first_method_call<'tcx>( + expr: &'tcx Expr<'tcx>, +) -> Option<(&'tcx PathSegment<'tcx>, &'tcx [Expr<'tcx>])> { + if let ExprKind::MethodCall(path, _, args, _) = &expr.kind { + if args.iter().any(|e| e.span.from_expansion()) { None } else { Some((path, *args)) } + } else { + None + } +} + +impl<'tcx> LateLintPass<'tcx> for TemporaryCStringAsPtr { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if in_macro(expr.span) { + return; + } + + match first_method_call(expr) { + Some((path, args)) if path.ident.name == sym::as_ptr => { + let unwrap_arg = &args[0]; + let as_ptr_span = path.ident.span; + match first_method_call(unwrap_arg) { + Some((path, args)) + if path.ident.name == sym::unwrap || path.ident.name == sym::expect => + { + let source_arg = &args[0]; + lint_cstring_as_ptr(cx, as_ptr_span, source_arg, unwrap_arg); + } + _ => return, + } + } + _ => return, + } + } +} + +fn lint_cstring_as_ptr( + cx: &LateContext<'_>, + as_ptr_span: Span, + source: &rustc_hir::Expr<'_>, + unwrap: &rustc_hir::Expr<'_>, +) { + let source_type = cx.typeck_results().expr_ty(source); + if let ty::Adt(def, substs) = source_type.kind() { + if cx.tcx.is_diagnostic_item(sym::result_type, def.did) { + if let ty::Adt(adt, _) = substs.type_at(0).kind() { + if cx.tcx.is_diagnostic_item(sym::cstring_type, adt.did) { + cx.struct_span_lint(TEMPORARY_CSTRING_AS_PTR, as_ptr_span, |diag| { + let mut diag = diag + .build("getting the inner pointer of a temporary `CString`"); + diag.span_label(as_ptr_span, "this pointer will be invalid"); + diag.span_label( + unwrap.span, + "this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime", + ); + diag.note("pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned"); + diag.help("for more information, see https://doc.rust-lang.org/reference/destructors.html"); + diag.emit(); + }); + } + } + } + } +} diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index b502bd7f7a1bd..bd0250305671e 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -439,7 +439,7 @@ fn lint_literal<'tcx>( cx.struct_span_lint(OVERFLOWING_LITERALS, e.span, |lint| { lint.build(&format!("literal out of range for `{}`", t.name_str())) .note(&format!( - "the literal `{}` does not fit into the type `{}` and will be converted to `std::{}::INFINITY`", + "the literal `{}` does not fit into the type `{}` and will be converted to `{}::INFINITY`", cx.sess() .source_map() .span_to_snippet(lit.span) diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 627adcceb3f4a..750f2e19ee257 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -717,7 +717,7 @@ options! {CodegenOptions, CodegenSetter, basic_codegen_options, // This list is in alphabetical order. // // If you add a new option, please update: - // - src/librustc_interface/tests.rs + // - compiler/rustc_interface/src/tests.rs // - src/doc/rustc/src/codegen-options/index.md ar: String = (String::new(), parse_string, [UNTRACKED], @@ -814,7 +814,7 @@ options! {CodegenOptions, CodegenSetter, basic_codegen_options, // This list is in alphabetical order. // // If you add a new option, please update: - // - src/librustc_interface/tests.rs + // - compiler/rustc_interface/src/tests.rs // - src/doc/rustc/src/codegen-options/index.md } @@ -825,7 +825,7 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, // This list is in alphabetical order. // // If you add a new option, please update: - // - src/librustc_interface/tests.rs + // - compiler/rustc_interface/src/tests.rs allow_features: Option> = (None, parse_opt_comma_list, [TRACKED], "only allow the listed language features to be enabled in code (space separated)"), @@ -904,6 +904,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "force all crates to be `rustc_private` unstable (default: no)"), fuel: Option<(String, u64)> = (None, parse_optimization_fuel, [TRACKED], "set the optimization fuel quota for a crate"), + function_sections: Option = (None, parse_opt_bool, [TRACKED], + "whether each function should go in its own section"), graphviz_dark_mode: bool = (false, parse_bool, [UNTRACKED], "use dark-themed colors in graphviz output (default: no)"), graphviz_font: String = ("Courier, monospace".to_string(), parse_string, [UNTRACKED], diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index d036c07804990..79363c3a5ca5c 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -738,14 +738,14 @@ impl Decodable for Span { } /// Calls the provided closure, using the provided `SourceMap` to format -/// any spans that are debug-printed during the closure'e exectuino. +/// any spans that are debug-printed during the closure's execution. /// /// Normally, the global `TyCtxt` is used to retrieve the `SourceMap` /// (see `rustc_interface::callbacks::span_debug1). However, some parts /// of the compiler (e.g. `rustc_parse`) may debug-print `Span`s before /// a `TyCtxt` is available. In this case, we fall back to /// the `SourceMap` provided to this function. If that is not available, -/// we fall back to printing the raw `Span` field values +/// we fall back to printing the raw `Span` field values. pub fn with_source_map T>(source_map: Lrc, f: F) -> T { SESSION_GLOBALS.with(|session_globals| { *session_globals.source_map.borrow_mut() = Some(source_map); @@ -1925,9 +1925,7 @@ impl HashStable for ExpnId { return; } - TAG_NOT_ROOT.hash_stable(ctx, hasher); let index = self.as_u32() as usize; - let res = CACHE.with(|cache| cache.borrow().get(index).copied().flatten()); if let Some(res) = res { @@ -1936,6 +1934,7 @@ impl HashStable for ExpnId { let new_len = index + 1; let mut sub_hasher = StableHasher::new(); + TAG_NOT_ROOT.hash_stable(ctx, &mut sub_hasher); self.expn_data().hash_stable(ctx, &mut sub_hasher); let sub_hash: Fingerprint = sub_hasher.finish(); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index ef0f09ae81895..34c00429cccd0 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -127,6 +127,7 @@ symbols! { ArgumentV1, Arguments, C, + CString, Center, Clone, Copy, @@ -261,6 +262,7 @@ symbols! { arm_target_feature, array, arrays, + as_ptr, as_str, asm, assert, @@ -310,6 +312,7 @@ symbols! { breakpoint, bridge, bswap, + c_str, c_variadic, call, call_mut, @@ -397,6 +400,7 @@ symbols! { crate_type, crate_visibility_modifier, crt_dash_static: "crt-static", + cstring_type, ctlz, ctlz_nonzero, ctpop, @@ -478,6 +482,7 @@ symbols! { existential_type, exp2f32, exp2f64, + expect, expected, expf32, expf64, @@ -501,6 +506,7 @@ symbols! { fadd_fast, fdiv_fast, feature, + ffi, ffi_const, ffi_pure, ffi_returns_twice, @@ -1170,6 +1176,7 @@ symbols! { unused_qualifications, unwind, unwind_attributes, + unwrap, unwrap_or, use_extern_macros, use_nested_groups, diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index c79b2624f8cb0..5f6d8ac751e26 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -85,8 +85,10 @@ pub fn is_const_evaluatable<'cx, 'tcx>( } else if leaf.has_param_types_or_consts() { failure_kind = cmp::min(failure_kind, FailureKind::MentionsParam); } + + false } - Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => (), + Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => false, }); match failure_kind { @@ -194,12 +196,12 @@ pub fn is_const_evaluatable<'cx, 'tcx>( /// /// This is only able to represent a subset of `MIR`, /// and should not leak any information about desugarings. -#[derive(Clone, Copy)] +#[derive(Debug, Clone, Copy)] pub struct AbstractConst<'tcx> { // FIXME: Consider adding something like `IndexSlice` // and use this here. - inner: &'tcx [Node<'tcx>], - substs: SubstsRef<'tcx>, + pub inner: &'tcx [Node<'tcx>], + pub substs: SubstsRef<'tcx>, } impl AbstractConst<'tcx> { @@ -209,9 +211,21 @@ impl AbstractConst<'tcx> { substs: SubstsRef<'tcx>, ) -> Result>, ErrorReported> { let inner = tcx.mir_abstract_const_opt_const_arg(def)?; + debug!("AbstractConst::new({:?}) = {:?}", def, inner); Ok(inner.map(|inner| AbstractConst { inner, substs })) } + pub fn from_const( + tcx: TyCtxt<'tcx>, + ct: &ty::Const<'tcx>, + ) -> Result>, ErrorReported> { + match ct.val { + ty::ConstKind::Unevaluated(def, substs, None) => AbstractConst::new(tcx, def, substs), + ty::ConstKind::Error(_) => Err(ErrorReported), + _ => Ok(None), + } + } + #[inline] pub fn subtree(self, node: NodeId) -> AbstractConst<'tcx> { AbstractConst { inner: &self.inner[..=node.index()], substs: self.substs } @@ -550,31 +564,32 @@ 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) +// FIXME: Use `std::ops::ControlFlow` instead of `bool` here. +pub fn walk_abstract_const<'tcx, F>(tcx: TyCtxt<'tcx>, ct: AbstractConst<'tcx>, mut f: F) -> bool where - F: FnMut(Node<'tcx>), + F: FnMut(Node<'tcx>) -> bool, { - recurse(tcx, ct, &mut f); - fn recurse<'tcx>(tcx: TyCtxt<'tcx>, ct: AbstractConst<'tcx>, f: &mut dyn FnMut(Node<'tcx>)) { + fn recurse<'tcx>( + tcx: TyCtxt<'tcx>, + ct: AbstractConst<'tcx>, + f: &mut dyn FnMut(Node<'tcx>) -> bool, + ) -> bool { 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); + f(root) + || match root { + Node::Leaf(_) => false, + 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) + || args.iter().any(|&arg| recurse(tcx, ct.subtree(arg), f)) } } - } } + + recurse(tcx, ct, &mut f) } /// Tries to unify two abstract constants using structural equality. diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index d1647e686a84f..d2ac24b6100da 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -11,6 +11,7 @@ use super::elaborate_predicates; use crate::infer::TyCtxtInferExt; +use crate::traits::const_evaluatable::{self, AbstractConst}; use crate::traits::query::evaluate_obligation::InferCtxtExt; use crate::traits::{self, Obligation, ObligationCause}; use rustc_errors::FatalError; @@ -249,7 +250,7 @@ fn predicates_reference_self( predicates .predicates .iter() - .map(|(predicate, sp)| (predicate.subst_supertrait(tcx, &trait_ref), *sp)) + .map(|&(predicate, sp)| (predicate.subst_supertrait(tcx, &trait_ref), sp)) .filter_map(|predicate| predicate_references_self(tcx, predicate)) .collect() } @@ -260,7 +261,7 @@ fn bounds_reference_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SmallVec<[Span .in_definition_order() .filter(|item| item.kind == ty::AssocKind::Type) .flat_map(|item| tcx.explicit_item_bounds(item.def_id)) - .map(|(predicate, sp)| (predicate.subst_supertrait(tcx, &trait_ref), *sp)) + .map(|&(predicate, sp)| (predicate.subst_supertrait(tcx, &trait_ref), sp)) .filter_map(|predicate| predicate_references_self(tcx, predicate)) .collect() } @@ -415,7 +416,7 @@ fn virtual_call_violation_for_method<'tcx>( )); } - for (i, input_ty) in sig.skip_binder().inputs()[1..].iter().enumerate() { + for (i, &input_ty) in sig.skip_binder().inputs()[1..].iter().enumerate() { if contains_illegal_self_type_reference(tcx, trait_def_id, input_ty) { return Some(MethodViolationCode::ReferencesSelfInput(i)); } @@ -438,10 +439,7 @@ fn virtual_call_violation_for_method<'tcx>( // so outlives predicates will always hold. .cloned() .filter(|(p, _)| p.to_opt_type_outlives().is_none()) - .collect::>() - // Do a shallow visit so that `contains_illegal_self_type_reference` - // may apply it's custom visiting. - .visit_tys_shallow(|t| contains_illegal_self_type_reference(tcx, trait_def_id, t)) + .any(|pred| contains_illegal_self_type_reference(tcx, trait_def_id, pred)) { return Some(MethodViolationCode::WhereClauseReferencesSelf); } @@ -715,10 +713,10 @@ fn receiver_is_dispatchable<'tcx>( }) } -fn contains_illegal_self_type_reference<'tcx>( +fn contains_illegal_self_type_reference<'tcx, T: TypeFoldable<'tcx>>( tcx: TyCtxt<'tcx>, trait_def_id: DefId, - ty: Ty<'tcx>, + value: T, ) -> bool { // This is somewhat subtle. In general, we want to forbid // references to `Self` in the argument and return types, @@ -761,7 +759,6 @@ fn contains_illegal_self_type_reference<'tcx>( struct IllegalSelfTypeVisitor<'tcx> { tcx: TyCtxt<'tcx>, - self_ty: Ty<'tcx>, trait_def_id: DefId, supertraits: Option>>, } @@ -769,7 +766,7 @@ fn contains_illegal_self_type_reference<'tcx>( impl<'tcx> TypeVisitor<'tcx> for IllegalSelfTypeVisitor<'tcx> { fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { match t.kind() { - ty::Param(_) => t == self.self_ty, + ty::Param(_) => t == self.tcx.types.self_param, ty::Projection(ref data) => { // This is a projected type `::X`. @@ -802,22 +799,62 @@ fn contains_illegal_self_type_reference<'tcx>( } } - fn visit_const(&mut self, _c: &ty::Const<'tcx>) -> bool { - // FIXME(#72219) Look into the unevaluated constants for object safety violations. - // Do not walk substitutions of unevaluated consts, as they contain `Self`, even - // though the const expression doesn't necessary use it. Currently type variables - // inside array length expressions are forbidden, so they can't break the above - // rules. - false + fn visit_const(&mut self, ct: &ty::Const<'tcx>) -> bool { + // First check if the type of this constant references `Self`. + if self.visit_ty(ct.ty) { + return true; + } + + // Constants can only influence object safety if they reference `Self`. + // This is only possible for unevaluated constants, so we walk these here. + // + // If `AbstractConst::new` returned an error we already failed compilation + // so we don't have to emit an additional error here. + // + // We currently recurse into abstract consts here but do not recurse in + // `is_const_evaluatable`. This means that the object safety check is more + // liberal than the const eval check. + // + // This shouldn't really matter though as we can't really use any + // constants which are not considered const evaluatable. + use rustc_middle::mir::abstract_const::Node; + if let Ok(Some(ct)) = AbstractConst::from_const(self.tcx, ct) { + const_evaluatable::walk_abstract_const(self.tcx, ct, |node| match node { + Node::Leaf(leaf) => { + let leaf = leaf.subst(self.tcx, ct.substs); + self.visit_const(leaf) + } + Node::Binop(..) | Node::UnaryOp(..) | Node::FunctionCall(_, _) => false, + }) + } else { + false + } + } + + fn visit_predicate(&mut self, pred: ty::Predicate<'tcx>) -> bool { + if let ty::PredicateAtom::ConstEvaluatable(def, substs) = pred.skip_binders() { + // FIXME(const_evaluatable_checked): We should probably deduplicate the logic for + // `AbstractConst`s here, it might make sense to change `ConstEvaluatable` to + // take a `ty::Const` instead. + use rustc_middle::mir::abstract_const::Node; + if let Ok(Some(ct)) = AbstractConst::new(self.tcx, def, substs) { + const_evaluatable::walk_abstract_const(self.tcx, ct, |node| match node { + Node::Leaf(leaf) => { + let leaf = leaf.subst(self.tcx, ct.substs); + self.visit_const(leaf) + } + Node::Binop(..) | Node::UnaryOp(..) | Node::FunctionCall(_, _) => false, + }) + } else { + false + } + } else { + pred.super_visit_with(self) + } } } - ty.visit_with(&mut IllegalSelfTypeVisitor { - tcx, - self_ty: tcx.types.self_param, - trait_def_id, - supertraits: None, - }) + value.visit_with(&mut IllegalSelfTypeVisitor { tcx, trait_def_id, supertraits: None }) } pub fn provide(providers: &mut ty::query::Providers) { diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index afc95eb47188c..136867d78f573 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -2090,25 +2090,25 @@ fn const_evaluatable_predicates_of<'tcx>( if let hir::Node::Item(item) = node { if let hir::ItemKind::Impl { ref of_trait, ref self_ty, .. } = item.kind { if let Some(of_trait) = of_trait { - warn!("const_evaluatable_predicates_of({:?}): visit impl trait_ref", def_id); + debug!("const_evaluatable_predicates_of({:?}): visit impl trait_ref", def_id); collector.visit_trait_ref(of_trait); } - warn!("const_evaluatable_predicates_of({:?}): visit_self_ty", def_id); + debug!("const_evaluatable_predicates_of({:?}): visit_self_ty", def_id); collector.visit_ty(self_ty); } } if let Some(generics) = node.generics() { - warn!("const_evaluatable_predicates_of({:?}): visit_generics", def_id); + debug!("const_evaluatable_predicates_of({:?}): visit_generics", def_id); collector.visit_generics(generics); } if let Some(fn_sig) = tcx.hir().fn_sig_by_hir_id(hir_id) { - warn!("const_evaluatable_predicates_of({:?}): visit_fn_decl", def_id); + debug!("const_evaluatable_predicates_of({:?}): visit_fn_decl", def_id); collector.visit_fn_decl(fn_sig.decl); } - warn!("const_evaluatable_predicates_of({:?}) = {:?}", def_id, collector.preds); + debug!("const_evaluatable_predicates_of({:?}) = {:?}", def_id, collector.preds); collector.preds } diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 281ed4f336c8b..3eb3eae64b9d7 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -16,7 +16,7 @@ cfg-if = { version = "0.1.8", features = ['rustc-dep-of-std'] } panic_unwind = { path = "../panic_unwind", optional = true } panic_abort = { path = "../panic_abort" } core = { path = "../core" } -libc = { version = "0.2.79", default-features = false, features = ['rustc-dep-of-std'] } +libc = { version = "0.2.80", default-features = false, features = ['rustc-dep-of-std'] } compiler_builtins = { version = "0.1.35" } profiler_builtins = { path = "../profiler_builtins", optional = true } unwind = { path = "../unwind" } diff --git a/library/std/src/ffi/c_str.rs b/library/std/src/ffi/c_str.rs index 6df4eb992594f..8c6d6c80402fa 100644 --- a/library/std/src/ffi/c_str.rs +++ b/library/std/src/ffi/c_str.rs @@ -110,6 +110,7 @@ use crate::sys; /// of `CString` instances can lead to invalid memory accesses, memory leaks, /// and other memory errors. #[derive(PartialEq, PartialOrd, Eq, Ord, Hash, Clone)] +#[cfg_attr(not(test), rustc_diagnostic_item = "cstring_type")] #[stable(feature = "rust1", since = "1.0.0")] pub struct CString { // Invariant 1: the slice ends with a zero byte and has a length of at least one. @@ -1265,7 +1266,7 @@ impl CStr { /// behavior when `ptr` is used inside the `unsafe` block: /// /// ```no_run - /// # #![allow(unused_must_use)] + /// # #![allow(unused_must_use)] #![cfg_attr(not(bootstrap), allow(temporary_cstring_as_ptr))] /// use std::ffi::CString; /// /// let ptr = CString::new("Hello").expect("CString::new failed").as_ptr(); diff --git a/src/test/ui/ast-json/issue-78398-foreign-ice.rs b/src/test/ui/ast-json/issue-78398-foreign-ice.rs new file mode 100644 index 0000000000000..a1a20f9568178 --- /dev/null +++ b/src/test/ui/ast-json/issue-78398-foreign-ice.rs @@ -0,0 +1,18 @@ +// Regression test for issue #78398 +// Tests that we don't ICE when trying to print the AST json +// when we have capturd tokens for a foreign item + +// check-pass +// compile-flags: -Zast-json + +fn main() {} + +macro_rules! mac_extern { + ($i:item) => { + extern "C" { $i } + } +} + +mac_extern! { + fn foo(); +} diff --git a/src/test/ui/ast-json/issue-78398-foreign-ice.stdout b/src/test/ui/ast-json/issue-78398-foreign-ice.stdout new file mode 100644 index 0000000000000..f1d0e44e9fc8c --- /dev/null +++ b/src/test/ui/ast-json/issue-78398-foreign-ice.stdout @@ -0,0 +1 @@ +{"module":{"inner":{"lo":192,"hi":315},"unsafety":"No","items":[{"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"prelude_import","span":{"lo":0,"hi":0}},"id":3,"args":null}],"tokens":null},"args":"Empty","tokens":null}]},"id":null,"style":"Outer","span":{"lo":0,"hi":0},"tokens":null}],"id":4,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"","span":{"lo":0,"hi":0}},"kind":{"variant":"Use","fields":[{"prefix":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"{{root}}","span":{"lo":0,"hi":0}},"id":5,"args":null},{"ident":{"name":"std","span":{"lo":0,"hi":0}},"id":6,"args":null},{"ident":{"name":"prelude","span":{"lo":0,"hi":0}},"id":7,"args":null},{"ident":{"name":"v1","span":{"lo":0,"hi":0}},"id":8,"args":null}],"tokens":null},"kind":"Glob","span":{"lo":0,"hi":0}}]},"tokens":null},{"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"macro_use","span":{"lo":0,"hi":0}},"id":9,"args":null}],"tokens":null},"args":"Empty","tokens":null}]},"id":null,"style":"Outer","span":{"lo":0,"hi":0},"tokens":null}],"id":10,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"std","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":null},{"attrs":[],"id":11,"span":{"lo":192,"hi":204},"vis":{"kind":"Inherited","span":{"lo":192,"hi":192},"tokens":null},"ident":{"name":"main","span":{"lo":195,"hi":199}},"kind":{"variant":"Fn","fields":["Final",{"header":{"unsafety":"No","asyncness":"No","constness":"No","ext":"None"},"decl":{"inputs":[],"output":{"variant":"Default","fields":[{"lo":202,"hi":202}]}},"span":{"lo":192,"hi":201}},{"params":[],"where_clause":{"has_where_token":false,"predicates":[],"span":{"lo":201,"hi":201}},"span":{"lo":199,"hi":199}},{"stmts":[],"id":12,"rules":"Default","span":{"lo":202,"hi":204},"tokens":null}]},"tokens":null},{"attrs":[],"id":13,"span":{"lo":206,"hi":284},"vis":{"kind":"Inherited","span":{"lo":206,"hi":206},"tokens":null},"ident":{"name":"mac_extern","span":{"lo":219,"hi":229}},"kind":{"variant":"MacroDef","fields":[{"body":{"variant":"Delimited","fields":[{"open":{"lo":230,"hi":231},"close":{"lo":283,"hi":284}},"Brace",{"0":[[{"variant":"Delimited","fields":[{"open":{"lo":236,"hi":237},"close":{"lo":244,"hi":245}},"Paren",{"0":[[{"variant":"Token","fields":[{"kind":"Dollar","span":{"lo":237,"hi":238}}]},"Alone"],[{"variant":"Token","fields":[{"kind":{"variant":"Ident","fields":["i",false]},"span":{"lo":238,"hi":239}}]},"Joint"],[{"variant":"Token","fields":[{"kind":"Colon","span":{"lo":239,"hi":240}}]},"Alone"],[{"variant":"Token","fields":[{"kind":{"variant":"Ident","fields":["item",false]},"span":{"lo":240,"hi":244}}]},"Alone"]]}]},"Alone"],[{"variant":"Token","fields":[{"kind":"FatArrow","span":{"lo":246,"hi":248}}]},"Alone"],[{"variant":"Delimited","fields":[{"open":{"lo":249,"hi":250},"close":{"lo":281,"hi":282}},"Brace",{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Ident","fields":["extern",false]},"span":{"lo":259,"hi":265}}]},"Alone"],[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"C","suffix":null}]},"span":{"lo":266,"hi":269}}]},"Alone"],[{"variant":"Delimited","fields":[{"open":{"lo":270,"hi":271},"close":{"lo":275,"hi":276}},"Brace",{"0":[[{"variant":"Token","fields":[{"kind":"Dollar","span":{"lo":272,"hi":273}}]},"Alone"],[{"variant":"Token","fields":[{"kind":{"variant":"Ident","fields":["i",false]},"span":{"lo":273,"hi":274}}]},"Alone"]]}]},"Alone"]]}]},"Alone"]]}]},"macro_rules":true}]},"tokens":null},{"attrs":[],"id":14,"span":{"lo":259,"hi":276},"vis":{"kind":"Inherited","span":{"lo":259,"hi":259},"tokens":null},"ident":{"name":"","span":{"lo":0,"hi":0}},"kind":{"variant":"ForeignMod","fields":[{"unsafety":"No","abi":{"style":"Cooked","symbol":"C","suffix":null,"span":{"lo":266,"hi":269},"symbol_unescaped":"C"},"items":[{"attrs":[],"id":15,"span":{"lo":304,"hi":313},"vis":{"kind":"Inherited","span":{"lo":304,"hi":304},"tokens":null},"ident":{"name":"foo","span":{"lo":307,"hi":310}},"kind":{"variant":"Fn","fields":["Final",{"header":{"unsafety":"No","asyncness":"No","constness":"No","ext":"None"},"decl":{"inputs":[],"output":{"variant":"Default","fields":[{"lo":312,"hi":312}]}},"span":{"lo":304,"hi":313}},{"params":[],"where_clause":{"has_where_token":false,"predicates":[],"span":{"lo":312,"hi":312}},"span":{"lo":310,"hi":310}},null]},"tokens":null}]}]},"tokens":null}],"inline":true},"attrs":[],"span":{"lo":192,"hi":315},"proc_macros":[]} diff --git a/src/test/ui/const-generics/const_evaluatable_checked/object-safety-err-ret.rs b/src/test/ui/const-generics/const_evaluatable_checked/object-safety-err-ret.rs new file mode 100644 index 0000000000000..5be4b41784c27 --- /dev/null +++ b/src/test/ui/const-generics/const_evaluatable_checked/object-safety-err-ret.rs @@ -0,0 +1,21 @@ +#![feature(const_generics, const_evaluatable_checked)] +#![allow(incomplete_features)] + + +const fn bar() -> usize { 7 } + +trait Foo { + fn test(&self) -> [u8; bar::()]; +} + +impl Foo for () { + fn test(&self) -> [u8; bar::()] { + [0; bar::()] + } +} + +fn use_dyn(v: &dyn Foo) { //~ERROR the trait `Foo` cannot be made into an object + v.test(); +} + +fn main() {} diff --git a/src/test/ui/const-generics/const_evaluatable_checked/object-safety-err-ret.stderr b/src/test/ui/const-generics/const_evaluatable_checked/object-safety-err-ret.stderr new file mode 100644 index 0000000000000..e0e6029252c00 --- /dev/null +++ b/src/test/ui/const-generics/const_evaluatable_checked/object-safety-err-ret.stderr @@ -0,0 +1,18 @@ +error[E0038]: the trait `Foo` cannot be made into an object + --> $DIR/object-safety-err-ret.rs:17:15 + | +LL | fn use_dyn(v: &dyn Foo) { + | ^^^^^^^^ `Foo` cannot be made into an object + | + = help: consider moving `test` to another trait +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit + --> $DIR/object-safety-err-ret.rs:8:23 + | +LL | trait Foo { + | --- this trait cannot be made into an object... +LL | fn test(&self) -> [u8; bar::()]; + | ^^^^^^^^^^^^^^^^^^^ ...because method `test` references the `Self` type in its return type + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0038`. diff --git a/src/test/ui/const-generics/const_evaluatable_checked/object-safety-err-where-bounds.rs b/src/test/ui/const-generics/const_evaluatable_checked/object-safety-err-where-bounds.rs new file mode 100644 index 0000000000000..5fbd4a5fa2e64 --- /dev/null +++ b/src/test/ui/const-generics/const_evaluatable_checked/object-safety-err-where-bounds.rs @@ -0,0 +1,22 @@ +#![feature(const_generics, const_evaluatable_checked)] +#![allow(incomplete_features)] +#![deny(where_clauses_object_safety)] + + +const fn bar() -> usize { 7 } + +trait Foo { + fn test(&self) where [u8; bar::()]: Sized; + //~^ ERROR the trait `Foo` cannot be made into an object + //~| WARN this was previously accepted by the compiler but is being phased out +} + +impl Foo for () { + fn test(&self) where [u8; bar::()]: Sized {} +} + +fn use_dyn(v: &dyn Foo) { + v.test(); +} + +fn main() {} diff --git a/src/test/ui/const-generics/const_evaluatable_checked/object-safety-err-where-bounds.stderr b/src/test/ui/const-generics/const_evaluatable_checked/object-safety-err-where-bounds.stderr new file mode 100644 index 0000000000000..45c7d835f339a --- /dev/null +++ b/src/test/ui/const-generics/const_evaluatable_checked/object-safety-err-where-bounds.stderr @@ -0,0 +1,24 @@ +error: the trait `Foo` cannot be made into an object + --> $DIR/object-safety-err-where-bounds.rs:9:8 + | +LL | fn test(&self) where [u8; bar::()]: Sized; + | ^^^^ + | +note: the lint level is defined here + --> $DIR/object-safety-err-where-bounds.rs:3:9 + | +LL | #![deny(where_clauses_object_safety)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #51443 +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit + --> $DIR/object-safety-err-where-bounds.rs:9:8 + | +LL | trait Foo { + | --- this trait cannot be made into an object... +LL | fn test(&self) where [u8; bar::()]: Sized; + | ^^^^ ...because method `test` references the `Self` type in its `where` clause + = help: consider moving `test` to another trait + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/const_evaluatable_checked/object-safety-ok-infer-err.rs b/src/test/ui/const-generics/const_evaluatable_checked/object-safety-ok-infer-err.rs new file mode 100644 index 0000000000000..9a95908d59d0d --- /dev/null +++ b/src/test/ui/const-generics/const_evaluatable_checked/object-safety-ok-infer-err.rs @@ -0,0 +1,22 @@ +#![feature(const_generics, const_evaluatable_checked)] +#![allow(incomplete_features)] + +trait Foo { + fn test(&self) -> [u8; N + 1]; +} + +impl Foo for () { + fn test(&self) -> [u8; N + 1] { + [0; N + 1] + } +} + +fn use_dyn(v: &dyn Foo) where [u8; N + 1]: Sized { + assert_eq!(v.test(), [0; N + 1]); +} + +fn main() { + // FIXME(const_evaluatable_checked): Improve the error message here. + use_dyn(&()); + //~^ ERROR type annotations needed +} diff --git a/src/test/ui/const-generics/const_evaluatable_checked/object-safety-ok-infer-err.stderr b/src/test/ui/const-generics/const_evaluatable_checked/object-safety-ok-infer-err.stderr new file mode 100644 index 0000000000000..dd2c11e42c5d2 --- /dev/null +++ b/src/test/ui/const-generics/const_evaluatable_checked/object-safety-ok-infer-err.stderr @@ -0,0 +1,12 @@ +error[E0284]: type annotations needed: cannot satisfy `the constant `use_dyn::<{_: usize}>::{constant#0}` can be evaluated` + --> $DIR/object-safety-ok-infer-err.rs:20:5 + | +LL | fn use_dyn(v: &dyn Foo) where [u8; N + 1]: Sized { + | ----- required by this bound in `use_dyn` +... +LL | use_dyn(&()); + | ^^^^^^^ cannot satisfy `the constant `use_dyn::<{_: usize}>::{constant#0}` can be evaluated` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0284`. diff --git a/src/test/ui/const-generics/const_evaluatable_checked/object-safety-ok.rs b/src/test/ui/const-generics/const_evaluatable_checked/object-safety-ok.rs new file mode 100644 index 0000000000000..ae78b7936a289 --- /dev/null +++ b/src/test/ui/const-generics/const_evaluatable_checked/object-safety-ok.rs @@ -0,0 +1,21 @@ +// run-pass +#![feature(const_generics, const_evaluatable_checked)] +#![allow(incomplete_features)] + +trait Foo { + fn test(&self) -> [u8; N + 1]; +} + +impl Foo for () { + fn test(&self) -> [u8; N + 1] { + [0; N + 1] + } +} + +fn use_dyn(v: &dyn Foo) where [u8; N + 1]: Sized { + assert_eq!(v.test(), [0; N + 1]); +} + +fn main() { + use_dyn::<3>(&()); +} diff --git a/src/test/ui/lint/lint-temporary-cstring-as-param.rs b/src/test/ui/lint/lint-temporary-cstring-as-param.rs new file mode 100644 index 0000000000000..9f5805367e43d --- /dev/null +++ b/src/test/ui/lint/lint-temporary-cstring-as-param.rs @@ -0,0 +1,11 @@ +#![deny(temporary_cstring_as_ptr)] + +use std::ffi::CString; +use std::os::raw::c_char; + +fn some_function(data: *const c_char) {} + +fn main() { + some_function(CString::new("").unwrap().as_ptr()); + //~^ ERROR getting the inner pointer of a temporary `CString` +} diff --git a/src/test/ui/lint/lint-temporary-cstring-as-param.stderr b/src/test/ui/lint/lint-temporary-cstring-as-param.stderr new file mode 100644 index 0000000000000..0a9e5a4bf4aa5 --- /dev/null +++ b/src/test/ui/lint/lint-temporary-cstring-as-param.stderr @@ -0,0 +1,18 @@ +error: getting the inner pointer of a temporary `CString` + --> $DIR/lint-temporary-cstring-as-param.rs:9:45 + | +LL | some_function(CString::new("").unwrap().as_ptr()); + | ------------------------- ^^^^^^ this pointer will be invalid + | | + | this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | +note: the lint level is defined here + --> $DIR/lint-temporary-cstring-as-param.rs:1:9 + | +LL | #![deny(temporary_cstring_as_ptr)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + = note: pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: for more information, see https://doc.rust-lang.org/reference/destructors.html + +error: aborting due to previous error + diff --git a/src/test/ui/lint/lint-temporary-cstring-as-ptr.rs b/src/test/ui/lint/lint-temporary-cstring-as-ptr.rs new file mode 100644 index 0000000000000..7aa4f2e1e005c --- /dev/null +++ b/src/test/ui/lint/lint-temporary-cstring-as-ptr.rs @@ -0,0 +1,9 @@ +// this program is not technically incorrect, but is an obscure enough style to be worth linting +#![deny(temporary_cstring_as_ptr)] + +use std::ffi::CString; + +fn main() { + let s = CString::new("some text").unwrap().as_ptr(); + //~^ ERROR getting the inner pointer of a temporary `CString` +} diff --git a/src/test/ui/lint/lint-temporary-cstring-as-ptr.stderr b/src/test/ui/lint/lint-temporary-cstring-as-ptr.stderr new file mode 100644 index 0000000000000..e69d2dd533a40 --- /dev/null +++ b/src/test/ui/lint/lint-temporary-cstring-as-ptr.stderr @@ -0,0 +1,18 @@ +error: getting the inner pointer of a temporary `CString` + --> $DIR/lint-temporary-cstring-as-ptr.rs:7:48 + | +LL | let s = CString::new("some text").unwrap().as_ptr(); + | ---------------------------------- ^^^^^^ this pointer will be invalid + | | + | this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | +note: the lint level is defined here + --> $DIR/lint-temporary-cstring-as-ptr.rs:2:9 + | +LL | #![deny(temporary_cstring_as_ptr)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + = note: pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: for more information, see https://doc.rust-lang.org/reference/destructors.html + +error: aborting due to previous error + diff --git a/src/test/ui/lint/lint-type-overflow2.stderr b/src/test/ui/lint/lint-type-overflow2.stderr index 61e33b7a260c9..0f16229a29178 100644 --- a/src/test/ui/lint/lint-type-overflow2.stderr +++ b/src/test/ui/lint/lint-type-overflow2.stderr @@ -17,7 +17,7 @@ error: literal out of range for `f32` LL | let x = -3.40282357e+38_f32; | ^^^^^^^^^^^^^^^^^^ | - = note: the literal `3.40282357e+38_f32` does not fit into the type `f32` and will be converted to `std::f32::INFINITY` + = note: the literal `3.40282357e+38_f32` does not fit into the type `f32` and will be converted to `f32::INFINITY` error: literal out of range for `f32` --> $DIR/lint-type-overflow2.rs:10:14 @@ -25,7 +25,7 @@ error: literal out of range for `f32` LL | let x = 3.40282357e+38_f32; | ^^^^^^^^^^^^^^^^^^ | - = note: the literal `3.40282357e+38_f32` does not fit into the type `f32` and will be converted to `std::f32::INFINITY` + = note: the literal `3.40282357e+38_f32` does not fit into the type `f32` and will be converted to `f32::INFINITY` error: literal out of range for `f64` --> $DIR/lint-type-overflow2.rs:11:14 @@ -33,7 +33,7 @@ error: literal out of range for `f64` LL | let x = -1.7976931348623159e+308_f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: the literal `1.7976931348623159e+308_f64` does not fit into the type `f64` and will be converted to `std::f64::INFINITY` + = note: the literal `1.7976931348623159e+308_f64` does not fit into the type `f64` and will be converted to `f64::INFINITY` error: literal out of range for `f64` --> $DIR/lint-type-overflow2.rs:12:14 @@ -41,7 +41,7 @@ error: literal out of range for `f64` LL | let x = 1.7976931348623159e+308_f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: the literal `1.7976931348623159e+308_f64` does not fit into the type `f64` and will be converted to `std::f64::INFINITY` + = note: the literal `1.7976931348623159e+308_f64` does not fit into the type `f64` and will be converted to `f64::INFINITY` error: aborting due to 5 previous errors diff --git a/src/tools/build-manifest/src/checksum.rs b/src/tools/build-manifest/src/checksum.rs new file mode 100644 index 0000000000000..c019c7a2f7aec --- /dev/null +++ b/src/tools/build-manifest/src/checksum.rs @@ -0,0 +1,97 @@ +use crate::manifest::{FileHash, Manifest}; +use rayon::prelude::*; +use sha2::{Digest, Sha256}; +use std::collections::{HashMap, HashSet}; +use std::error::Error; +use std::fs::File; +use std::io::BufReader; +use std::path::{Path, PathBuf}; +use std::sync::Mutex; +use std::time::Instant; + +pub(crate) struct Checksums { + cache_path: Option, + collected: Mutex>, +} + +impl Checksums { + pub(crate) fn new() -> Result> { + let cache_path = std::env::var_os("BUILD_MANIFEST_CHECKSUM_CACHE").map(PathBuf::from); + + let mut collected = HashMap::new(); + if let Some(path) = &cache_path { + if path.is_file() { + collected = serde_json::from_slice(&std::fs::read(path)?)?; + } + } + + Ok(Checksums { cache_path, collected: Mutex::new(collected) }) + } + + pub(crate) fn store_cache(&self) -> Result<(), Box> { + if let Some(path) = &self.cache_path { + std::fs::write(path, &serde_json::to_vec(&self.collected)?)?; + } + Ok(()) + } + + pub(crate) fn fill_missing_checksums(&mut self, manifest: &mut Manifest) { + let need_checksums = self.find_missing_checksums(manifest); + if !need_checksums.is_empty() { + self.collect_checksums(&need_checksums); + } + self.replace_checksums(manifest); + } + + fn find_missing_checksums(&mut self, manifest: &mut Manifest) -> HashSet { + let collected = self.collected.lock().unwrap(); + let mut need_checksums = HashSet::new(); + crate::manifest::visit_file_hashes(manifest, |file_hash| { + if let FileHash::Missing(path) = file_hash { + let path = std::fs::canonicalize(path).unwrap(); + if !collected.contains_key(&path) { + need_checksums.insert(path); + } + } + }); + need_checksums + } + + fn replace_checksums(&mut self, manifest: &mut Manifest) { + let collected = self.collected.lock().unwrap(); + crate::manifest::visit_file_hashes(manifest, |file_hash| { + if let FileHash::Missing(path) = file_hash { + let path = std::fs::canonicalize(path).unwrap(); + match collected.get(&path) { + Some(hash) => *file_hash = FileHash::Present(hash.clone()), + None => panic!("missing hash for file {}", path.display()), + } + } + }); + } + + fn collect_checksums(&mut self, files: &HashSet) { + let collection_start = Instant::now(); + println!( + "collecting hashes for {} tarballs across {} threads", + files.len(), + rayon::current_num_threads().min(files.len()), + ); + + files.par_iter().for_each(|path| match hash(path) { + Ok(hash) => { + self.collected.lock().unwrap().insert(path.clone(), hash); + } + Err(err) => eprintln!("error while fetching the hash for {}: {}", path.display(), err), + }); + + println!("collected {} hashes in {:.2?}", files.len(), collection_start.elapsed()); + } +} + +fn hash(path: &Path) -> Result> { + let mut file = BufReader::new(File::open(path)?); + let mut sha256 = Sha256::default(); + std::io::copy(&mut file, &mut sha256)?; + Ok(hex::encode(sha256.finalize())) +} diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs index ffcf10571ca7d..2863216855b83 100644 --- a/src/tools/build-manifest/src/main.rs +++ b/src/tools/build-manifest/src/main.rs @@ -4,22 +4,19 @@ //! via `x.py dist hash-and-sign`; the cmdline arguments are set up //! by rustbuild (in `src/bootstrap/dist.rs`). +mod checksum; mod manifest; mod versions; -use crate::manifest::{Component, FileHash, Manifest, Package, Rename, Target}; +use crate::checksum::Checksums; +use crate::manifest::{Component, Manifest, Package, Rename, Target}; use crate::versions::{PkgType, Versions}; -use rayon::prelude::*; -use sha2::Digest; use std::collections::{BTreeMap, HashMap, HashSet}; use std::env; -use std::error::Error; use std::fs::{self, File}; -use std::io::{self, BufReader, Read, Write}; +use std::io::{self, Read, Write}; use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; -use std::sync::Mutex; -use std::time::Instant; static HOSTS: &[&str] = &[ "aarch64-apple-darwin", @@ -186,6 +183,7 @@ macro_rules! t { struct Builder { versions: Versions, + checksums: Checksums, shipped_files: HashSet, input: PathBuf, @@ -240,6 +238,7 @@ fn main() { Builder { versions: Versions::new(&channel, &input).unwrap(), + checksums: t!(Checksums::new()), shipped_files: HashSet::new(), input, @@ -276,6 +275,8 @@ impl Builder { if let Some(path) = std::env::var_os("BUILD_MANIFEST_SHIPPED_FILES_PATH") { self.write_shipped_files(&Path::new(&path)); } + + t!(self.checksums.store_cache()); } /// If a tool does not pass its tests, don't ship it. @@ -321,7 +322,7 @@ impl Builder { self.add_renames_to(&mut manifest); manifest.pkg.insert("rust".to_string(), self.rust_package(&manifest)); - self.fill_missing_hashes(&mut manifest); + self.checksums.fill_missing_checksums(&mut manifest); manifest } @@ -595,41 +596,6 @@ impl Builder { assert!(t!(child.wait()).success()); } - fn fill_missing_hashes(&self, manifest: &mut Manifest) { - // First collect all files that need hashes - let mut need_hashes = HashSet::new(); - crate::manifest::visit_file_hashes(manifest, |file_hash| { - if let FileHash::Missing(path) = file_hash { - need_hashes.insert(path.clone()); - } - }); - - let collected = Mutex::new(HashMap::new()); - let collection_start = Instant::now(); - println!( - "collecting hashes for {} tarballs across {} threads", - need_hashes.len(), - rayon::current_num_threads().min(need_hashes.len()), - ); - need_hashes.par_iter().for_each(|path| match fetch_hash(path) { - Ok(hash) => { - collected.lock().unwrap().insert(path, hash); - } - Err(err) => eprintln!("error while fetching the hash for {}: {}", path.display(), err), - }); - let collected = collected.into_inner().unwrap(); - println!("collected {} hashes in {:.2?}", collected.len(), collection_start.elapsed()); - - crate::manifest::visit_file_hashes(manifest, |file_hash| { - if let FileHash::Missing(path) = file_hash { - match collected.get(path) { - Some(hash) => *file_hash = FileHash::Present(hash.clone()), - None => panic!("missing hash for file {}", path.display()), - } - } - }) - } - fn write_channel_files(&mut self, channel_name: &str, manifest: &Manifest) { self.write(&toml::to_string(&manifest).unwrap(), channel_name, ".toml"); self.write(&manifest.date, channel_name, "-date.txt"); @@ -660,10 +626,3 @@ impl Builder { t!(std::fs::write(path, content.as_bytes())); } } - -fn fetch_hash(path: &Path) -> Result> { - let mut file = BufReader::new(File::open(path)?); - let mut sha256 = sha2::Sha256::default(); - std::io::copy(&mut file, &mut sha256)?; - Ok(hex::encode(sha256.finalize())) -} diff --git a/src/tools/clippy/.github/driver.sh b/src/tools/clippy/.github/driver.sh index 2c17c4203ae5c..fc4dca5042b2a 100644 --- a/src/tools/clippy/.github/driver.sh +++ b/src/tools/clippy/.github/driver.sh @@ -22,9 +22,9 @@ unset CARGO_MANIFEST_DIR # Run a lint and make sure it produces the expected output. It's also expected to exit with code 1 # FIXME: How to match the clippy invocation in compile-test.rs? -./target/debug/clippy-driver -Dwarnings -Aunused -Zui-testing --emit metadata --crate-type bin tests/ui/cstring.rs 2> cstring.stderr && exit 1 -sed -e "s,tests/ui,\$DIR," -e "/= help/d" cstring.stderr > normalized.stderr -diff normalized.stderr tests/ui/cstring.stderr +./target/debug/clippy-driver -Dwarnings -Aunused -Zui-testing --emit metadata --crate-type bin tests/ui/cast.rs 2> cast.stderr && exit 1 +sed -e "s,tests/ui,\$DIR," -e "/= help/d" cast.stderr > normalized.stderr +diff normalized.stderr tests/ui/cast.stderr # make sure "clippy-driver --rustc --arg" and "rustc --arg" behave the same diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index d4d2f92a6a695..b5ca63cefec4f 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -707,7 +707,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::SKIP_WHILE_NEXT, &methods::STRING_EXTEND_CHARS, &methods::SUSPICIOUS_MAP, - &methods::TEMPORARY_CSTRING_AS_PTR, &methods::UNINIT_ASSUMED_INIT, &methods::UNNECESSARY_FILTER_MAP, &methods::UNNECESSARY_FOLD, @@ -1417,7 +1416,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::SKIP_WHILE_NEXT), LintId::of(&methods::STRING_EXTEND_CHARS), LintId::of(&methods::SUSPICIOUS_MAP), - LintId::of(&methods::TEMPORARY_CSTRING_AS_PTR), LintId::of(&methods::UNINIT_ASSUMED_INIT), LintId::of(&methods::UNNECESSARY_FILTER_MAP), LintId::of(&methods::UNNECESSARY_FOLD), @@ -1765,7 +1763,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&mem_replace::MEM_REPLACE_WITH_UNINIT), LintId::of(&methods::CLONE_DOUBLE_REF), LintId::of(&methods::ITERATOR_STEP_BY_ZERO), - LintId::of(&methods::TEMPORARY_CSTRING_AS_PTR), LintId::of(&methods::UNINIT_ASSUMED_INIT), LintId::of(&methods::ZST_OFFSET), LintId::of(&minmax::MIN_MAX), diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index c0824bacbc735..d250bfd71e936 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -798,40 +798,6 @@ declare_clippy_lint! { "using a single-character str where a char could be used, e.g., `_.split(\"x\")`" } -declare_clippy_lint! { - /// **What it does:** Checks for getting the inner pointer of a temporary - /// `CString`. - /// - /// **Why is this bad?** The inner pointer of a `CString` is only valid as long - /// as the `CString` is alive. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// # use std::ffi::CString; - /// # fn call_some_ffi_func(_: *const i8) {} - /// # - /// let c_str = CString::new("foo").unwrap().as_ptr(); - /// unsafe { - /// call_some_ffi_func(c_str); - /// } - /// ``` - /// Here `c_str` points to a freed address. The correct use would be: - /// ```rust - /// # use std::ffi::CString; - /// # fn call_some_ffi_func(_: *const i8) {} - /// # - /// let c_str = CString::new("foo").unwrap(); - /// unsafe { - /// call_some_ffi_func(c_str.as_ptr()); - /// } - /// ``` - pub TEMPORARY_CSTRING_AS_PTR, - correctness, - "getting the inner pointer of a temporary `CString`" -} - declare_clippy_lint! { /// **What it does:** Checks for calling `.step_by(0)` on iterators which panics. /// @@ -1406,7 +1372,6 @@ declare_lint_pass!(Methods => [ SINGLE_CHAR_PATTERN, SINGLE_CHAR_PUSH_STR, SEARCH_IS_SOME, - TEMPORARY_CSTRING_AS_PTR, FILTER_NEXT, SKIP_WHILE_NEXT, FILTER_MAP, @@ -1490,7 +1455,6 @@ impl<'tcx> LateLintPass<'tcx> for Methods { lint_search_is_some(cx, expr, "rposition", arg_lists[1], arg_lists[0], method_spans[1]) }, ["extend", ..] => lint_extend(cx, expr, arg_lists[0]), - ["as_ptr", "unwrap" | "expect"] => lint_cstring_as_ptr(cx, expr, &arg_lists[1][0], &arg_lists[0][0]), ["nth", "iter"] => lint_iter_nth(cx, expr, &arg_lists, false), ["nth", "iter_mut"] => lint_iter_nth(cx, expr, &arg_lists, true), ["nth", ..] => lint_iter_nth_zero(cx, expr, arg_lists[0]), @@ -2207,26 +2171,6 @@ fn lint_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_> } } -fn lint_cstring_as_ptr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, source: &hir::Expr<'_>, unwrap: &hir::Expr<'_>) { - if_chain! { - let source_type = cx.typeck_results().expr_ty(source); - if let ty::Adt(def, substs) = source_type.kind(); - if cx.tcx.is_diagnostic_item(sym!(result_type), def.did); - if match_type(cx, substs.type_at(0), &paths::CSTRING); - then { - span_lint_and_then( - cx, - TEMPORARY_CSTRING_AS_PTR, - expr.span, - "you are getting the inner pointer of a temporary `CString`", - |diag| { - diag.note("that pointer will be invalid outside this expression"); - diag.span_help(unwrap.span, "assign the `CString` to a variable to extend its lifetime"); - }); - } - } -} - fn lint_iter_cloned_collect<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, iter_args: &'tcx [hir::Expr<'_>]) { if_chain! { if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym!(vec_type)); diff --git a/src/tools/clippy/clippy_lints/src/utils/paths.rs b/src/tools/clippy/clippy_lints/src/utils/paths.rs index 5e769c690a690..7566da80982a0 100644 --- a/src/tools/clippy/clippy_lints/src/utils/paths.rs +++ b/src/tools/clippy/clippy_lints/src/utils/paths.rs @@ -21,7 +21,6 @@ pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"]; pub const CMP_MAX: [&str; 3] = ["core", "cmp", "max"]; pub const CMP_MIN: [&str; 3] = ["core", "cmp", "min"]; pub const COW: [&str; 3] = ["alloc", "borrow", "Cow"]; -pub const CSTRING: [&str; 4] = ["std", "ffi", "c_str", "CString"]; pub const CSTRING_AS_C_STR: [&str; 5] = ["std", "ffi", "c_str", "CString", "as_c_str"]; pub const DEFAULT_TRAIT: [&str; 3] = ["core", "default", "Default"]; pub const DEFAULT_TRAIT_METHOD: [&str; 4] = ["core", "default", "Default", "default"]; diff --git a/src/tools/clippy/src/lintlist/mod.rs b/src/tools/clippy/src/lintlist/mod.rs index 6301d623a2b12..dcbb8a6a31da9 100644 --- a/src/tools/clippy/src/lintlist/mod.rs +++ b/src/tools/clippy/src/lintlist/mod.rs @@ -2258,13 +2258,6 @@ vec![ deprecation: None, module: "temporary_assignment", }, - Lint { - name: "temporary_cstring_as_ptr", - group: "correctness", - desc: "getting the inner pointer of a temporary `CString`", - deprecation: None, - module: "methods", - }, Lint { name: "to_digit_is_some", group: "style", diff --git a/src/tools/clippy/tests/ui/cstring.rs b/src/tools/clippy/tests/ui/cstring.rs deleted file mode 100644 index 6cdd6b4ff6e77..0000000000000 --- a/src/tools/clippy/tests/ui/cstring.rs +++ /dev/null @@ -1,24 +0,0 @@ -#![deny(clippy::temporary_cstring_as_ptr)] - -fn main() {} - -fn temporary_cstring() { - use std::ffi::CString; - - CString::new("foo").unwrap().as_ptr(); - CString::new("foo").expect("dummy").as_ptr(); -} - -mod issue4375 { - use std::ffi::CString; - use std::os::raw::c_char; - - extern "C" { - fn foo(data: *const c_char); - } - - pub fn bar(v: &[u8]) { - let cstr = CString::new(v); - unsafe { foo(cstr.unwrap().as_ptr()) } - } -} diff --git a/src/tools/clippy/tests/ui/cstring.stderr b/src/tools/clippy/tests/ui/cstring.stderr deleted file mode 100644 index 87cb29be57758..0000000000000 --- a/src/tools/clippy/tests/ui/cstring.stderr +++ /dev/null @@ -1,46 +0,0 @@ -error: you are getting the inner pointer of a temporary `CString` - --> $DIR/cstring.rs:8:5 - | -LL | CString::new("foo").unwrap().as_ptr(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: the lint level is defined here - --> $DIR/cstring.rs:1:9 - | -LL | #![deny(clippy::temporary_cstring_as_ptr)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: that pointer will be invalid outside this expression -help: assign the `CString` to a variable to extend its lifetime - --> $DIR/cstring.rs:8:5 - | -LL | CString::new("foo").unwrap().as_ptr(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: you are getting the inner pointer of a temporary `CString` - --> $DIR/cstring.rs:9:5 - | -LL | CString::new("foo").expect("dummy").as_ptr(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: that pointer will be invalid outside this expression -help: assign the `CString` to a variable to extend its lifetime - --> $DIR/cstring.rs:9:5 - | -LL | CString::new("foo").expect("dummy").as_ptr(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: you are getting the inner pointer of a temporary `CString` - --> $DIR/cstring.rs:22:22 - | -LL | unsafe { foo(cstr.unwrap().as_ptr()) } - | ^^^^^^^^^^^^^^^^^^^^^^ - | - = note: that pointer will be invalid outside this expression -help: assign the `CString` to a variable to extend its lifetime - --> $DIR/cstring.rs:22:22 - | -LL | unsafe { foo(cstr.unwrap().as_ptr()) } - | ^^^^^^^^^^^^^ - -error: aborting due to 3 previous errors -