diff options
530 files changed, 6734 insertions, 2655 deletions
diff --git a/.mailmap b/.mailmap index 93f5ca8157c..0bd16a797ca 100644 --- a/.mailmap +++ b/.mailmap @@ -307,6 +307,8 @@ Joseph T. Lyons <JosephTLyons@gmail.com> <JosephTLyons@users.noreply.github.com> Josh Cotton <jcotton42@outlook.com> Josh Driver <keeperofdakeys@gmail.com> Josh Holmer <jholmer.in@gmail.com> +Josh Stone <cuviper@gmail.com> <jistone@redhat.com> +Josh Stone <cuviper@gmail.com> <jistone@fedoraproject.org> Julian Knodt <julianknodt@gmail.com> jumbatm <jumbatm@gmail.com> <30644300+jumbatm@users.noreply.github.com> Junyoung Cho <june0.cho@samsung.com> @@ -474,7 +476,8 @@ Philipp Matthias Schäfer <philipp.matthias.schaefer@posteo.de> phosphorus <steepout@qq.com> Pierre Krieger <pierre.krieger1708@gmail.com> pierwill <pierwill@users.noreply.github.com> <19642016+pierwill@users.noreply.github.com> -Pietro Albini <pietro@pietroalbini.org> <pietro@pietroalbini.io> <pietro.albini@ferrous-systems.com> +Pietro Albini <pietro@pietroalbini.org> <pietro@pietroalbini.io> +Pietro Albini <pietro@pietroalbini.org> <pietro.albini@ferrous-systems.com> Pradyumna Rahul <prkinformed@gmail.com> PrzemysÅ‚aw WesoÅ‚ek <jest@go.art.pl> Przemek WesoÅ‚ek <jest@go.art.pl> r00ster <r00ster91@protonmail.com> @@ -543,6 +546,7 @@ Takashi Idobe <idobetakashi@gmail.com> Takayuki Maeda <takoyaki0316@gmail.com> Tamir Duberstein <tamird@gmail.com> Tamir Duberstein <tamird@squareup.com> Tatsuyuki Ishi <ishitatsuyuki@gmail.com> +Tau Gärtli <git@tau.garden> <ruben.schmidmeister@icloud.com> Tero Hänninen <lgvz@users.noreply.github.com> Tero Hänninen <tejohann@kapsi.fi> The8472 <git@infinite-source.de> Theo Belaire <theo.belaire@gmail.com> Theo Belaire <tyr.god.of.war.42@gmail.com> diff --git a/Cargo.lock b/Cargo.lock index b1bdaef81ff..c211024e1ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -469,9 +469,9 @@ version = "0.1.0" [[package]] name = "cc" -version = "1.0.90" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" +checksum = "2678b2e3449475e95b0aa6f9b506a28e61b3dc8996592b983695e8ebb58a8b41" [[package]] name = "cfg-if" @@ -4263,7 +4263,6 @@ dependencies = [ "either", "field-offset", "gsgdt", - "measureme", "polonius-engine", "rustc-rayon", "rustc-rayon-core", @@ -5580,7 +5579,6 @@ dependencies = [ name = "tidy" version = "0.1.0" dependencies = [ - "cargo-platform", "cargo_metadata 0.15.4", "ignore", "lazy_static", diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 63ce6685e43..5b708cf4e1a 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -920,14 +920,8 @@ impl BinOpKind { matches!(self, BinOpKind::And | BinOpKind::Or) } - pub fn is_comparison(&self) -> bool { - use BinOpKind::*; - // Note for developers: please keep this match exhaustive; - // we want compilation to fail if another variant is added. - match *self { - Eq | Lt | Le | Ne | Gt | Ge => true, - And | Or | Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Shl | Shr => false, - } + pub fn is_comparison(self) -> bool { + crate::util::parser::AssocOp::from_ast_binop(self).is_comparison() } /// Returns `true` if the binary operator takes its arguments by value. diff --git a/compiler/rustc_ast_ir/src/lib.rs b/compiler/rustc_ast_ir/src/lib.rs index ff7a1552047..9ff2e32f06b 100644 --- a/compiler/rustc_ast_ir/src/lib.rs +++ b/compiler/rustc_ast_ir/src/lib.rs @@ -51,6 +51,14 @@ impl Mutability { } } + /// Returns `"const"` or `"mut"` depending on the mutability. + pub fn ptr_str(self) -> &'static str { + match self { + Mutability::Not => "const", + Mutability::Mut => "mut", + } + } + /// Returns `""` (empty string) or `"mutably "` depending on the mutability. pub fn mutably_str(self) -> &'static str { match self { diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index ac3799e7a05..a3731e94276 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -273,6 +273,7 @@ ast_passes_visibility_not_permitted = .trait_impl = trait items always share the visibility of their trait .individual_impl_items = place qualifiers on individual impl items instead .individual_foreign_items = place qualifiers on individual foreign items instead + .remove_qualifier_sugg = remove the qualifier ast_passes_where_clause_after_type_alias = where clauses are not allowed after the type for type aliases .note = see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 01addc8127e..cb4dcf3ae75 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -266,7 +266,11 @@ impl<'a> AstValidator<'a> { return; } - self.dcx().emit_err(errors::VisibilityNotPermitted { span: vis.span, note }); + self.dcx().emit_err(errors::VisibilityNotPermitted { + span: vis.span, + note, + remove_qualifier_sugg: vis.span, + }); } fn check_decl_no_pat(decl: &FnDecl, mut report_err: impl FnMut(Span, Option<Ident>, bool)) { diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index 8ae9f7d3966..f397c949e04 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -31,6 +31,12 @@ pub struct VisibilityNotPermitted { pub span: Span, #[subdiagnostic] pub note: VisibilityNotPermittedNote, + #[suggestion( + ast_passes_remove_qualifier_sugg, + code = "", + applicability = "machine-applicable" + )] + pub remove_qualifier_sugg: Span, } #[derive(Subdiagnostic)] diff --git a/compiler/rustc_borrowck/messages.ftl b/compiler/rustc_borrowck/messages.ftl index 587536e1f9a..c14a617eb91 100644 --- a/compiler/rustc_borrowck/messages.ftl +++ b/compiler/rustc_borrowck/messages.ftl @@ -87,6 +87,12 @@ borrowck_move_unsized = borrowck_moved_a_fn_once_in_call = this value implements `FnOnce`, which causes it to be moved when called +borrowck_moved_a_fn_once_in_call_call = + `FnOnce` closures can only be called once + +borrowck_moved_a_fn_once_in_call_def = + `{$ty}` is made to be an `FnOnce` closure here + borrowck_moved_due_to_await = {$place_name} {$is_partial -> [true] partially moved diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 62e16d445c6..48cd9c268a1 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -203,13 +203,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if !seen_spans.contains(&move_span) { if !closure { - self.suggest_ref_or_clone( - mpi, - move_span, - &mut err, - &mut in_pattern, - move_spans, - ); + self.suggest_ref_or_clone(mpi, &mut err, &mut in_pattern, move_spans); } let msg_opt = CapturedMessageOpt { @@ -283,7 +277,19 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { Some(name) => format!("`{name}`"), None => "value".to_owned(), }; - if self.suggest_borrow_fn_like(&mut err, ty, &move_site_vec, ¬e_msg) { + if self.suggest_borrow_fn_like(&mut err, ty, &move_site_vec, ¬e_msg) + || if let UseSpans::FnSelfUse { kind, .. } = use_spans + && let CallKind::FnCall { fn_trait_id, self_ty } = kind + && let ty::Param(_) = self_ty.kind() + && ty == self_ty + && Some(fn_trait_id) == self.infcx.tcx.lang_items().fn_once_trait() + { + // this is a type parameter `T: FnOnce()`, don't suggest `T: FnOnce() + Clone`. + true + } else { + false + } + { // Suppress the next suggestion since we don't want to put more bounds onto // something that already has `Fn`-like bounds (or is a closure), so we can't // restrict anyways. @@ -339,18 +345,28 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { fn suggest_ref_or_clone( &self, mpi: MovePathIndex, - move_span: Span, err: &mut Diag<'tcx>, in_pattern: &mut bool, move_spans: UseSpans<'_>, ) { + let move_span = match move_spans { + UseSpans::ClosureUse { capture_kind_span, .. } => capture_kind_span, + _ => move_spans.args_or_use(), + }; struct ExpressionFinder<'hir> { expr_span: Span, expr: Option<&'hir hir::Expr<'hir>>, pat: Option<&'hir hir::Pat<'hir>>, parent_pat: Option<&'hir hir::Pat<'hir>>, + hir: rustc_middle::hir::map::Map<'hir>, } impl<'hir> Visitor<'hir> for ExpressionFinder<'hir> { + type NestedFilter = OnlyBodies; + + fn nested_visit_map(&mut self) -> Self::Map { + self.hir + } + fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) { if e.span == self.expr_span { self.expr = Some(e); @@ -385,8 +401,13 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let expr = hir.body(body_id).value; let place = &self.move_data.move_paths[mpi].place; let span = place.as_local().map(|local| self.body.local_decls[local].source_info.span); - let mut finder = - ExpressionFinder { expr_span: move_span, expr: None, pat: None, parent_pat: None }; + let mut finder = ExpressionFinder { + expr_span: move_span, + expr: None, + pat: None, + parent_pat: None, + hir, + }; finder.visit_expr(expr); if let Some(span) = span && let Some(expr) = finder.expr @@ -467,16 +488,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } else if let UseSpans::ClosureUse { closure_kind: ClosureKind::Coroutine(CoroutineKind::Desugared(_, CoroutineSource::Block)), - args_span: _, - capture_kind_span: _, - path_span, + .. } = move_spans { - self.suggest_cloning(err, ty, expr, path_span); + self.suggest_cloning(err, ty, expr, None); } else if self.suggest_hoisting_call_outside_loop(err, expr) { // The place where the the type moves would be misleading to suggest clone. // #121466 - self.suggest_cloning(err, ty, expr, move_span); + self.suggest_cloning(err, ty, expr, None); } } if let Some(pat) = finder.pat { @@ -652,6 +671,68 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { err } + fn ty_kind_suggestion(&self, ty: Ty<'tcx>) -> Option<String> { + // Keep in sync with `rustc_hir_analysis/src/check/mod.rs:ty_kind_suggestion`. + // FIXME: deduplicate the above. + let tcx = self.infcx.tcx; + let implements_default = |ty| { + let Some(default_trait) = tcx.get_diagnostic_item(sym::Default) else { + return false; + }; + self.infcx + .type_implements_trait(default_trait, [ty], self.param_env) + .must_apply_modulo_regions() + }; + + Some(match ty.kind() { + ty::Never | ty::Error(_) => return None, + ty::Bool => "false".to_string(), + ty::Char => "\'x\'".to_string(), + ty::Int(_) | ty::Uint(_) => "42".into(), + ty::Float(_) => "3.14159".into(), + ty::Slice(_) => "[]".to_string(), + ty::Adt(def, _) if Some(def.did()) == tcx.get_diagnostic_item(sym::Vec) => { + "vec![]".to_string() + } + ty::Adt(def, _) if Some(def.did()) == tcx.get_diagnostic_item(sym::String) => { + "String::new()".to_string() + } + ty::Adt(def, args) if def.is_box() => { + format!("Box::new({})", self.ty_kind_suggestion(args[0].expect_ty())?) + } + ty::Adt(def, _) if Some(def.did()) == tcx.get_diagnostic_item(sym::Option) => { + "None".to_string() + } + ty::Adt(def, args) if Some(def.did()) == tcx.get_diagnostic_item(sym::Result) => { + format!("Ok({})", self.ty_kind_suggestion(args[0].expect_ty())?) + } + ty::Adt(_, _) if implements_default(ty) => "Default::default()".to_string(), + ty::Ref(_, ty, mutability) => { + if let (ty::Str, hir::Mutability::Not) = (ty.kind(), mutability) { + "\"\"".to_string() + } else { + let Some(ty) = self.ty_kind_suggestion(*ty) else { + return None; + }; + format!("&{}{ty}", mutability.prefix_str()) + } + } + ty::Array(ty, len) => format!( + "[{}; {}]", + self.ty_kind_suggestion(*ty)?, + len.eval_target_usize(tcx, ty::ParamEnv::reveal_all()), + ), + ty::Tuple(tys) => format!( + "({})", + tys.iter() + .map(|ty| self.ty_kind_suggestion(ty)) + .collect::<Option<Vec<String>>>()? + .join(", ") + ), + _ => "value".to_string(), + }) + } + fn suggest_assign_value( &self, err: &mut Diag<'_>, @@ -661,34 +742,16 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let ty = moved_place.ty(self.body, self.infcx.tcx).ty; debug!("ty: {:?}, kind: {:?}", ty, ty.kind()); - let tcx = self.infcx.tcx; - let implements_default = |ty, param_env| { - let Some(default_trait) = tcx.get_diagnostic_item(sym::Default) else { - return false; - }; - self.infcx - .type_implements_trait(default_trait, [ty], param_env) - .must_apply_modulo_regions() - }; - - let assign_value = match ty.kind() { - ty::Bool => "false", - ty::Float(_) => "0.0", - ty::Int(_) | ty::Uint(_) => "0", - ty::Never | ty::Error(_) => "", - ty::Adt(def, _) if Some(def.did()) == tcx.get_diagnostic_item(sym::Vec) => "vec![]", - ty::Adt(_, _) if implements_default(ty, self.param_env) => "Default::default()", - _ => "todo!()", + let Some(assign_value) = self.ty_kind_suggestion(ty) else { + return; }; - if !assign_value.is_empty() { - err.span_suggestion_verbose( - sugg_span.shrink_to_hi(), - "consider assigning a value", - format!(" = {assign_value}"), - Applicability::MaybeIncorrect, - ); - } + err.span_suggestion_verbose( + sugg_span.shrink_to_hi(), + "consider assigning a value", + format!(" = {assign_value}"), + Applicability::MaybeIncorrect, + ); } fn suggest_borrow_fn_like( @@ -916,7 +979,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { sm.span_to_diagnostic_string(span) } }; - let mut spans: MultiSpan = spans.clone().into(); + let mut spans: MultiSpan = spans.into(); // Point at all the `continue`s and explicit `break`s in the relevant loops. for (desc, elements) in [ ("`break` exits", &finder.found_breaks), @@ -987,8 +1050,272 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { can_suggest_clone } - fn suggest_cloning(&self, err: &mut Diag<'_>, ty: Ty<'tcx>, expr: &hir::Expr<'_>, span: Span) { + /// We have `S { foo: val, ..base }`, and we suggest instead writing + /// `S { foo: val, bar: base.bar.clone(), .. }` when valid. + fn suggest_cloning_on_functional_record_update( + &self, + err: &mut Diag<'_>, + ty: Ty<'tcx>, + expr: &'cx hir::Expr<'cx>, + ) { + let typeck_results = self.infcx.tcx.typeck(self.mir_def_id()); + let hir::ExprKind::Struct(struct_qpath, fields, Some(base)) = expr.kind else { return }; + let hir::QPath::Resolved(_, path) = struct_qpath else { return }; + let hir::def::Res::Def(_, def_id) = path.res else { return }; + let Some(expr_ty) = typeck_results.node_type_opt(expr.hir_id) else { return }; + let ty::Adt(def, args) = expr_ty.kind() else { return }; + let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = base.kind else { return }; + let (hir::def::Res::Local(_) + | hir::def::Res::Def( + DefKind::Const | DefKind::ConstParam | DefKind::Static { .. } | DefKind::AssocConst, + _, + )) = path.res + else { + return; + }; + let Ok(base_str) = self.infcx.tcx.sess.source_map().span_to_snippet(base.span) else { + return; + }; + + // 1. look for the fields of type `ty`. + // 2. check if they are clone and add them to suggestion + // 3. check if there are any values left to `..` and remove it if not + // 4. emit suggestion to clone the field directly as `bar: base.bar.clone()` + + let mut final_field_count = fields.len(); + let Some(variant) = def.variants().iter().find(|variant| variant.def_id == def_id) else { + // When we have an enum, look for the variant that corresponds to the variant the user + // wrote. + return; + }; + let mut sugg = vec![]; + for field in &variant.fields { + // In practice unless there are more than one field with the same type, we'll be + // suggesting a single field at a type, because we don't aggregate multiple borrow + // checker errors involving the functional record update sytnax into a single one. + let field_ty = field.ty(self.infcx.tcx, args); + let ident = field.ident(self.infcx.tcx); + if field_ty == ty && fields.iter().all(|field| field.ident.name != ident.name) { + // Suggest adding field and cloning it. + sugg.push(format!("{ident}: {base_str}.{ident}.clone()")); + final_field_count += 1; + } + } + let (span, sugg) = match fields { + [.., last] => ( + if final_field_count == variant.fields.len() { + // We'll remove the `..base` as there aren't any fields left. + last.span.shrink_to_hi().with_hi(base.span.hi()) + } else { + last.span.shrink_to_hi() + }, + format!(", {}", sugg.join(", ")), + ), + // Account for no fields in suggestion span. + [] => ( + expr.span.with_lo(struct_qpath.span().hi()), + if final_field_count == variant.fields.len() { + // We'll remove the `..base` as there aren't any fields left. + format!(" {{ {} }}", sugg.join(", ")) + } else { + format!(" {{ {}, ..{base_str} }}", sugg.join(", ")) + }, + ), + }; + let prefix = if !self.implements_clone(ty) { + let msg = format!("`{ty}` doesn't implement `Copy` or `Clone`"); + if let ty::Adt(def, _) = ty.kind() { + err.span_note(self.infcx.tcx.def_span(def.did()), msg); + } else { + err.note(msg); + } + format!("if `{ty}` implemented `Clone`, you could ") + } else { + String::new() + }; + let msg = format!( + "{prefix}clone the value from the field instead of using the functional record update \ + syntax", + ); + err.span_suggestion_verbose(span, msg, sugg, Applicability::MachineApplicable); + } + + pub(crate) fn suggest_cloning( + &self, + err: &mut Diag<'_>, + ty: Ty<'tcx>, + mut expr: &'cx hir::Expr<'cx>, + mut other_expr: Option<&'cx hir::Expr<'cx>>, + ) { + if let hir::ExprKind::Struct(_, _, Some(_)) = expr.kind { + // We have `S { foo: val, ..base }`. In `check_aggregate_rvalue` we have a single + // `Location` that covers both the `S { ... }` literal, all of its fields and the + // `base`. If the move happens because of `S { foo: val, bar: base.bar }` the `expr` + // will already be correct. Instead, we see if we can suggest writing. + self.suggest_cloning_on_functional_record_update(err, ty, expr); + return; + } + + if let Some(some_other_expr) = other_expr + && let Some(parent_binop) = + self.infcx.tcx.hir().parent_iter(expr.hir_id).find_map(|n| { + if let (hir_id, hir::Node::Expr(e)) = n + && let hir::ExprKind::AssignOp(_binop, target, _arg) = e.kind + && target.hir_id == expr.hir_id + { + Some(hir_id) + } else { + None + } + }) + && let Some(other_parent_binop) = + self.infcx.tcx.hir().parent_iter(some_other_expr.hir_id).find_map(|n| { + if let (hir_id, hir::Node::Expr(expr)) = n + && let hir::ExprKind::AssignOp(..) = expr.kind + { + Some(hir_id) + } else { + None + } + }) + && parent_binop == other_parent_binop + { + // Explicitly look for `expr += other_expr;` and avoid suggesting + // `expr.clone() += other_expr;`, instead suggesting `expr += other_expr.clone();`. + other_expr = Some(expr); + expr = some_other_expr; + } + 'outer: { + if let ty::Ref(..) = ty.kind() { + // We check for either `let binding = foo(expr, other_expr);` or + // `foo(expr, other_expr);` and if so we don't suggest an incorrect + // `foo(expr, other_expr).clone()` + if let Some(other_expr) = other_expr + && let Some(parent_let) = + self.infcx.tcx.hir().parent_iter(expr.hir_id).find_map(|n| { + if let (hir_id, hir::Node::LetStmt(_) | hir::Node::Stmt(_)) = n { + Some(hir_id) + } else { + None + } + }) + && let Some(other_parent_let) = + self.infcx.tcx.hir().parent_iter(other_expr.hir_id).find_map(|n| { + if let (hir_id, hir::Node::LetStmt(_) | hir::Node::Stmt(_)) = n { + Some(hir_id) + } else { + None + } + }) + && parent_let == other_parent_let + { + // Explicitly check that we don't have `foo(&*expr, other_expr)`, as cloning the + // result of `foo(...)` won't help. + break 'outer; + } + + // We're suggesting `.clone()` on an borrowed value. See if the expression we have + // is an argument to a function or method call, and try to suggest cloning the + // *result* of the call, instead of the argument. This is closest to what people + // would actually be looking for in most cases, with maybe the exception of things + // like `fn(T) -> T`, but even then it is reasonable. + let typeck_results = self.infcx.tcx.typeck(self.mir_def_id()); + let mut prev = expr; + while let hir::Node::Expr(parent) = self.infcx.tcx.parent_hir_node(prev.hir_id) { + if let hir::ExprKind::Call(..) | hir::ExprKind::MethodCall(..) = parent.kind + && let Some(call_ty) = typeck_results.node_type_opt(parent.hir_id) + && let call_ty = call_ty.peel_refs() + && (!call_ty + .walk() + .any(|t| matches!(t.unpack(), ty::GenericArgKind::Lifetime(_))) + || if let ty::Alias(ty::Projection, _) = call_ty.kind() { + // FIXME: this isn't quite right with lifetimes on assoc types, + // but ignore for now. We will only suggest cloning if + // `<Ty as Trait>::Assoc: Clone`, which should keep false positives + // down to a managable ammount. + true + } else { + false + }) + && self.implements_clone(call_ty) + && self.suggest_cloning_inner(err, call_ty, parent) + { + return; + } + prev = parent; + } + } + } + let ty = ty.peel_refs(); + if self.implements_clone(ty) { + self.suggest_cloning_inner(err, ty, expr); + } else if let ty::Adt(def, args) = ty.kind() + && def.did().as_local().is_some() + && def.variants().iter().all(|variant| { + variant + .fields + .iter() + .all(|field| self.implements_clone(field.ty(self.infcx.tcx, args))) + }) + { + err.span_note( + self.infcx.tcx.def_span(def.did()), + format!("if `{ty}` implemented `Clone`, you could clone the value"), + ); + } + } + + fn implements_clone(&self, ty: Ty<'tcx>) -> bool { + let Some(clone_trait_def) = self.infcx.tcx.lang_items().clone_trait() else { return false }; + self.infcx + .type_implements_trait(clone_trait_def, [ty], self.param_env) + .must_apply_modulo_regions() + } + + /// Given an expression, check if it is a method call `foo.clone()`, where `foo` and + /// `foo.clone()` both have the same type, returning the span for `.clone()` if so. + pub(crate) fn clone_on_reference(&self, expr: &hir::Expr<'_>) -> Option<Span> { + let typeck_results = self.infcx.tcx.typeck(self.mir_def_id()); + if let hir::ExprKind::MethodCall(segment, rcvr, args, span) = expr.kind + && let Some(expr_ty) = typeck_results.node_type_opt(expr.hir_id) + && let Some(rcvr_ty) = typeck_results.node_type_opt(rcvr.hir_id) + && rcvr_ty == expr_ty + && segment.ident.name == sym::clone + && args.is_empty() + { + Some(span) + } else { + None + } + } + + fn in_move_closure(&self, expr: &hir::Expr<'_>) -> bool { + for (_, node) in self.infcx.tcx.hir().parent_iter(expr.hir_id) { + if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(closure), .. }) = node + && let hir::CaptureBy::Value { .. } = closure.capture_clause + { + // `move || x.clone()` will not work. FIXME: suggest `let y = x.clone(); move || y` + return true; + } + } + false + } + + fn suggest_cloning_inner( + &self, + err: &mut Diag<'_>, + ty: Ty<'tcx>, + expr: &hir::Expr<'_>, + ) -> bool { let tcx = self.infcx.tcx; + if let Some(_) = self.clone_on_reference(expr) { + // Avoid redundant clone suggestion already suggested in `explain_captures`. + // See `tests/ui/moves/needs-clone-through-deref.rs` + return false; + } + if self.in_move_closure(expr) { + return false; + } // Try to find predicates on *generic params* that would allow copying `ty` let suggestion = if let Some(symbol) = tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) { @@ -996,27 +1323,39 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } else { ".clone()".to_owned() }; - if let Some(clone_trait_def) = tcx.lang_items().clone_trait() - && self - .infcx - .type_implements_trait(clone_trait_def, [ty], self.param_env) - .must_apply_modulo_regions() + let mut sugg = Vec::with_capacity(2); + let mut inner_expr = expr; + // Remove uses of `&` and `*` when suggesting `.clone()`. + while let hir::ExprKind::AddrOf(.., inner) | hir::ExprKind::Unary(hir::UnOp::Deref, inner) = + &inner_expr.kind { - let msg = if let ty::Adt(def, _) = ty.kind() - && [tcx.get_diagnostic_item(sym::Arc), tcx.get_diagnostic_item(sym::Rc)] - .contains(&Some(def.did())) - { - "clone the value to increment its reference count" - } else { - "consider cloning the value if the performance cost is acceptable" - }; - err.span_suggestion_verbose( - span.shrink_to_hi(), - msg, - suggestion, - Applicability::MachineApplicable, - ); + if let hir::ExprKind::AddrOf(_, hir::Mutability::Mut, _) = inner_expr.kind { + // We assume that `&mut` refs are desired for their side-effects, so cloning the + // value wouldn't do what the user wanted. + return false; + } + inner_expr = inner; + } + if inner_expr.span.lo() != expr.span.lo() { + sugg.push((expr.span.with_hi(inner_expr.span.lo()), String::new())); } + let span = if inner_expr.span.hi() != expr.span.hi() { + // Account for `(*x)` to suggest `x.clone()`. + expr.span.with_lo(inner_expr.span.hi()) + } else { + expr.span.shrink_to_hi() + }; + sugg.push((span, suggestion)); + let msg = if let ty::Adt(def, _) = ty.kind() + && [tcx.get_diagnostic_item(sym::Arc), tcx.get_diagnostic_item(sym::Rc)] + .contains(&Some(def.did())) + { + "clone the value to increment its reference count" + } else { + "consider cloning the value if the performance cost is acceptable" + }; + err.multipart_suggestion_verbose(msg, sugg, Applicability::MachineApplicable); + true } fn suggest_adding_bounds(&self, err: &mut Diag<'_>, ty: Ty<'tcx>, def_id: DefId, span: Span) { @@ -1121,6 +1460,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { None, ); self.suggest_copy_for_type_in_cloned_ref(&mut err, place); + let typeck_results = self.infcx.tcx.typeck(self.mir_def_id()); + if let Some(expr) = self.find_expr(borrow_span) + && let Some(ty) = typeck_results.node_type_opt(expr.hir_id) + { + self.suggest_cloning(&mut err, ty, expr, self.find_expr(span)); + } self.buffer_error(err); } @@ -1469,27 +1814,31 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let hir = tcx.hir(); let Some(body_id) = tcx.hir_node(self.mir_hir_id()).body_id() else { return }; struct FindUselessClone<'hir> { + tcx: TyCtxt<'hir>, + def_id: DefId, pub clones: Vec<&'hir hir::Expr<'hir>>, } impl<'hir> FindUselessClone<'hir> { - pub fn new() -> Self { - Self { clones: vec![] } + pub fn new(tcx: TyCtxt<'hir>, def_id: DefId) -> Self { + Self { tcx, def_id, clones: vec![] } } } impl<'v> Visitor<'v> for FindUselessClone<'v> { fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { - // FIXME: use `lookup_method_for_diagnostic`? if let hir::ExprKind::MethodCall(segment, _rcvr, args, _span) = ex.kind && segment.ident.name == sym::clone && args.len() == 0 + && let Some(def_id) = self.def_id.as_local() + && let Some(method) = self.tcx.lookup_method_for_diagnostic((def_id, ex.hir_id)) + && Some(self.tcx.parent(method)) == self.tcx.lang_items().clone_trait() { self.clones.push(ex); } hir::intravisit::walk_expr(self, ex); } } - let mut expr_finder = FindUselessClone::new(); + let mut expr_finder = FindUselessClone::new(tcx, self.mir_def_id().into()); let body = hir.body(body_id).value; expr_finder.visit_expr(body); @@ -1538,22 +1887,33 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } for ty in types_to_constrain { - self.suggest_adding_bounds(err, ty, clone, body.span); - if let ty::Adt(..) = ty.kind() { - // The type doesn't implement Clone. - let trait_ref = ty::Binder::dummy(ty::TraitRef::new(self.infcx.tcx, clone, [ty])); - let obligation = Obligation::new( - self.infcx.tcx, - ObligationCause::dummy(), - self.param_env, - trait_ref, - ); - self.infcx.err_ctxt().suggest_derive( - &obligation, - err, - trait_ref.to_predicate(self.infcx.tcx), - ); - } + self.suggest_adding_bounds_or_derive(err, ty, clone, body.span); + } + } + + pub(crate) fn suggest_adding_bounds_or_derive( + &self, + err: &mut Diag<'_>, + ty: Ty<'tcx>, + trait_def_id: DefId, + span: Span, + ) { + self.suggest_adding_bounds(err, ty, trait_def_id, span); + if let ty::Adt(..) = ty.kind() { + // The type doesn't implement the trait. + let trait_ref = + ty::Binder::dummy(ty::TraitRef::new(self.infcx.tcx, trait_def_id, [ty])); + let obligation = Obligation::new( + self.infcx.tcx, + ObligationCause::dummy(), + self.param_env, + trait_ref, + ); + self.infcx.err_ctxt().suggest_derive( + &obligation, + err, + trait_ref.to_predicate(self.infcx.tcx), + ); } } @@ -1654,6 +2014,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ); } + pub(crate) fn find_expr(&self, span: Span) -> Option<&hir::Expr<'_>> { + let tcx = self.infcx.tcx; + let body_id = tcx.hir_node(self.mir_hir_id()).body_id()?; + let mut expr_finder = FindExprBySpan::new(span); + expr_finder.visit_expr(tcx.hir().body(body_id).value); + expr_finder.result + } + fn suggest_slice_method_if_applicable( &self, err: &mut Diag<'_>, diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 0106e285604..dbea317e7bb 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -5,6 +5,7 @@ use crate::session_diagnostics::{ CaptureVarKind, CaptureVarPathUseCause, OnClosureNote, }; use rustc_errors::{Applicability, Diag}; +use rustc_errors::{DiagCtxt, MultiSpan}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, Namespace}; use rustc_hir::CoroutineKind; @@ -29,6 +30,8 @@ use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt as _; use rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions; +use crate::fluent_generated as fluent; + use super::borrow_set::BorrowData; use super::MirBorrowckCtxt; @@ -587,7 +590,7 @@ impl UseSpans<'_> { #[allow(rustc::diagnostic_outside_of_impl)] pub(super) fn args_subdiag( self, - dcx: &rustc_errors::DiagCtxt, + dcx: &DiagCtxt, err: &mut Diag<'_>, f: impl FnOnce(Span) -> CaptureArgLabel, ) { @@ -601,7 +604,7 @@ impl UseSpans<'_> { #[allow(rustc::diagnostic_outside_of_impl)] pub(super) fn var_path_only_subdiag( self, - dcx: &rustc_errors::DiagCtxt, + dcx: &DiagCtxt, err: &mut Diag<'_>, action: crate::InitializationRequiringAction, ) { @@ -639,7 +642,7 @@ impl UseSpans<'_> { #[allow(rustc::diagnostic_outside_of_impl)] pub(super) fn var_subdiag( self, - dcx: &rustc_errors::DiagCtxt, + dcx: &DiagCtxt, err: &mut Diag<'_>, kind: Option<rustc_middle::mir::BorrowKind>, f: impl FnOnce(hir::ClosureKind, Span) -> CaptureVarCause, @@ -1034,7 +1037,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { .map(|n| format!("`{n}`")) .unwrap_or_else(|| "value".to_owned()); match kind { - CallKind::FnCall { fn_trait_id, .. } + CallKind::FnCall { fn_trait_id, self_ty } if Some(fn_trait_id) == self.infcx.tcx.lang_items().fn_once_trait() => { err.subdiagnostic( @@ -1046,7 +1049,79 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { is_loop_message, }, ); - err.subdiagnostic(self.dcx(), CaptureReasonNote::FnOnceMoveInCall { var_span }); + // Check if the move occurs on a value because of a call on a closure that comes + // from a type parameter `F: FnOnce()`. If so, we provide a targeted `note`: + // ``` + // error[E0382]: use of moved value: `blk` + // --> $DIR/once-cant-call-twice-on-heap.rs:8:5 + // | + // LL | fn foo<F:FnOnce()>(blk: F) { + // | --- move occurs because `blk` has type `F`, which does not implement the `Copy` trait + // LL | blk(); + // | ----- `blk` moved due to this call + // LL | blk(); + // | ^^^ value used here after move + // | + // note: `FnOnce` closures can only be called once + // --> $DIR/once-cant-call-twice-on-heap.rs:6:10 + // | + // LL | fn foo<F:FnOnce()>(blk: F) { + // | ^^^^^^^^ `F` is made to be an `FnOnce` closure here + // LL | blk(); + // | ----- this value implements `FnOnce`, which causes it to be moved when called + // ``` + if let ty::Param(param_ty) = self_ty.kind() + && let generics = self.infcx.tcx.generics_of(self.mir_def_id()) + && let param = generics.type_param(param_ty, self.infcx.tcx) + && let Some(hir_generics) = self + .infcx + .tcx + .typeck_root_def_id(self.mir_def_id().to_def_id()) + .as_local() + .and_then(|def_id| self.infcx.tcx.hir().get_generics(def_id)) + && let spans = hir_generics + .predicates + .iter() + .filter_map(|pred| match pred { + hir::WherePredicate::BoundPredicate(pred) => Some(pred), + _ => None, + }) + .filter(|pred| { + if let Some((id, _)) = pred.bounded_ty.as_generic_param() { + id == param.def_id + } else { + false + } + }) + .flat_map(|pred| pred.bounds) + .filter_map(|bound| { + if let Some(trait_ref) = bound.trait_ref() + && let Some(trait_def_id) = trait_ref.trait_def_id() + && trait_def_id == fn_trait_id + { + Some(bound.span()) + } else { + None + } + }) + .collect::<Vec<Span>>() + && !spans.is_empty() + { + let mut span: MultiSpan = spans.clone().into(); + for sp in spans { + span.push_span_label(sp, fluent::borrowck_moved_a_fn_once_in_call_def); + } + span.push_span_label( + fn_call_span, + fluent::borrowck_moved_a_fn_once_in_call, + ); + err.span_note(span, fluent::borrowck_moved_a_fn_once_in_call_call); + } else { + err.subdiagnostic( + self.dcx(), + CaptureReasonNote::FnOnceMoveInCall { var_span }, + ); + } } CallKind::Operator { self_arg, trait_id, .. } => { let self_arg = self_arg.unwrap(); @@ -1212,13 +1287,21 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { .iter_projections() .any(|(_, elem)| matches!(elem, ProjectionElem::Deref)) { + let (start, end) = if let Some(expr) = self.find_expr(move_span) + && let Some(_) = self.clone_on_reference(expr) + && let hir::ExprKind::MethodCall(_, rcvr, _, _) = expr.kind + { + (move_span.shrink_to_lo(), move_span.with_lo(rcvr.span.hi())) + } else { + (move_span.shrink_to_lo(), move_span.shrink_to_hi()) + }; vec![ // We use the fully-qualified path because `.clone()` can // sometimes choose `<&T as Clone>` instead of `<T as Clone>` // when going through auto-deref, so this ensures that doesn't // happen, causing suggestions for `.clone().clone()`. - (move_span.shrink_to_lo(), format!("<{ty} as Clone>::clone(&")), - (move_span.shrink_to_hi(), ")".to_string()), + (start, format!("<{ty} as Clone>::clone(&")), + (end, ")".to_string()), ] } else { vec![(move_span.shrink_to_hi(), ".clone()".to_string())] diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index 0d1b875cbed..bc02c5be93d 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -435,7 +435,9 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { fn add_move_hints(&self, error: GroupedMoveError<'tcx>, err: &mut Diag<'_>, span: Span) { match error { - GroupedMoveError::MovesFromPlace { mut binds_to, move_from, .. } => { + GroupedMoveError::MovesFromPlace { + mut binds_to, move_from, span: other_span, .. + } => { self.add_borrow_suggestions(err, span); if binds_to.is_empty() { let place_ty = move_from.ty(self.body, self.infcx.tcx).ty; @@ -444,6 +446,10 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { None => "value".to_string(), }; + if let Some(expr) = self.find_expr(span) { + self.suggest_cloning(err, place_ty, expr, self.find_expr(other_span)); + } + err.subdiagnostic( self.dcx(), crate::session_diagnostics::TypeNoCopy::Label { @@ -468,19 +474,24 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } // No binding. Nothing to suggest. GroupedMoveError::OtherIllegalMove { ref original_path, use_spans, .. } => { - let span = use_spans.var_or_use(); + let use_span = use_spans.var_or_use(); let place_ty = original_path.ty(self.body, self.infcx.tcx).ty; let place_desc = match self.describe_place(original_path.as_ref()) { Some(desc) => format!("`{desc}`"), None => "value".to_string(), }; + + if let Some(expr) = self.find_expr(use_span) { + self.suggest_cloning(err, place_ty, expr, self.find_expr(span)); + } + err.subdiagnostic( self.dcx(), crate::session_diagnostics::TypeNoCopy::Label { is_partial_move: false, ty: place_ty, place: &place_desc, - span, + span: use_span, }, ); @@ -582,6 +593,11 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { if binds_to.len() == 1 { let place_desc = &format!("`{}`", self.local_names[*local].unwrap()); + + if let Some(expr) = self.find_expr(binding_span) { + self.suggest_cloning(err, bind_to.ty, expr, None); + } + err.subdiagnostic( self.dcx(), crate::session_diagnostics::TypeNoCopy::Label { diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index bd068b29c12..602a84ce4dd 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -21,7 +21,7 @@ use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt; use crate::diagnostics::BorrowedContentSource; use crate::util::FindAssignments; -use crate::MirBorrowckCtxt; +use crate::{session_diagnostics, MirBorrowckCtxt}; #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub(crate) enum AccessKind { @@ -234,7 +234,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { Some(mir::BorrowKind::Mut { kind: mir::MutBorrowKind::Default }), |_kind, var_span| { let place = self.describe_any_place(access_place.as_ref()); - crate::session_diagnostics::CaptureVarCause::MutableBorrowUsePlaceClosure { + session_diagnostics::CaptureVarCause::MutableBorrowUsePlaceClosure { place, var_span, } @@ -667,19 +667,26 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { /// User cannot make signature of a trait mutable without changing the /// trait. So we find if this error belongs to a trait and if so we move /// suggestion to the trait or disable it if it is out of scope of this crate - fn is_error_in_trait(&self, local: Local) -> (bool, Option<Span>) { + /// + /// The returned values are: + /// - is the current item an assoc `fn` of an impl that corresponds to a trait def? if so, we + /// have to suggest changing both the impl `fn` arg and the trait `fn` arg + /// - is the trait from the local crate? If not, we can't suggest changing signatures + /// - `Span` of the argument in the trait definition + fn is_error_in_trait(&self, local: Local) -> (bool, bool, Option<Span>) { if self.body.local_kind(local) != LocalKind::Arg { - return (false, None); + return (false, false, None); } let my_def = self.body.source.def_id(); let my_hir = self.infcx.tcx.local_def_id_to_hir_id(my_def.as_local().unwrap()); let Some(td) = self.infcx.tcx.impl_of_method(my_def).and_then(|x| self.infcx.tcx.trait_id_of_impl(x)) else { - return (false, None); + return (false, false, None); }; ( true, + td.is_local(), td.as_local().and_then(|tld| match self.infcx.tcx.hir_node_by_def_id(tld) { Node::Item(hir::Item { kind: hir::ItemKind::Trait(_, _, _, _, items), .. }) => { let mut f_in_trait_opt = None; @@ -695,19 +702,16 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { break; } f_in_trait_opt.and_then(|f_in_trait| { - match self.infcx.tcx.hir_node(f_in_trait) { - Node::TraitItem(hir::TraitItem { - kind: - hir::TraitItemKind::Fn( - hir::FnSig { decl: hir::FnDecl { inputs, .. }, .. }, - _, - ), - .. - }) => { - let hir::Ty { span, .. } = *inputs.get(local.index() - 1)?; - Some(span) - } - _ => None, + if let Node::TraitItem(ti) = self.infcx.tcx.hir_node(f_in_trait) + && let hir::TraitItemKind::Fn(sig, _) = ti.kind + && let Some(ty) = sig.decl.inputs.get(local.index() - 1) + && let hir::TyKind::Ref(_, mut_ty) = ty.kind + && let hir::Mutability::Not = mut_ty.mutbl + && sig.decl.implicit_self.has_implicit_self() + { + Some(ty.span) + } else { + None } }) } @@ -1061,20 +1065,24 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let (pointer_sigil, pointer_desc) = if local_decl.ty.is_ref() { ("&", "reference") } else { ("*const", "pointer") }; - let (is_trait_sig, local_trait) = self.is_error_in_trait(local); - if is_trait_sig && local_trait.is_none() { + let (is_trait_sig, is_local, local_trait) = self.is_error_in_trait(local); + + if is_trait_sig && !is_local { + // Do not suggest to change the signature when the trait comes from another crate. + err.span_label( + local_decl.source_info.span, + format!("this is an immutable {pointer_desc}"), + ); return; } - - let decl_span = match local_trait { - Some(span) => span, - None => local_decl.source_info.span, - }; + let decl_span = local_decl.source_info.span; let label = match *local_decl.local_info() { LocalInfo::User(mir::BindingForm::ImplicitSelf(_)) => { let suggestion = suggest_ampmut_self(self.infcx.tcx, decl_span); - Some((true, decl_span, suggestion)) + let additional = + local_trait.map(|span| (span, suggest_ampmut_self(self.infcx.tcx, span))); + Some((true, decl_span, suggestion, additional)) } LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm { @@ -1113,7 +1121,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { // don't create labels for compiler-generated spans Some(_) => None, None => { - let label = if name != kw::SelfLower { + let (has_sugg, decl_span, sugg) = if name != kw::SelfLower { suggest_ampmut( self.infcx.tcx, local_decl.ty, @@ -1140,7 +1148,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { ), } }; - Some(label) + Some((has_sugg, decl_span, sugg, None)) } } } @@ -1151,22 +1159,33 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { })) => { let pattern_span: Span = local_decl.source_info.span; suggest_ref_mut(self.infcx.tcx, pattern_span) - .map(|span| (true, span, "mut ".to_owned())) + .map(|span| (true, span, "mut ".to_owned(), None)) } _ => unreachable!(), }; match label { - Some((true, err_help_span, suggested_code)) => { - err.span_suggestion_verbose( - err_help_span, - format!("consider changing this to be a mutable {pointer_desc}"), - suggested_code, + Some((true, err_help_span, suggested_code, additional)) => { + let mut sugg = vec![(err_help_span, suggested_code)]; + if let Some(s) = additional { + sugg.push(s); + } + + err.multipart_suggestion_verbose( + format!( + "consider changing this to be a mutable {pointer_desc}{}", + if is_trait_sig { + " in the `impl` method and the `trait` definition" + } else { + "" + } + ), + sugg, Applicability::MachineApplicable, ); } - Some((false, err_label_span, message)) => { + Some((false, err_label_span, message, _)) => { let def_id = self.body.source.def_id(); let hir_id = if let Some(local_def_id) = def_id.as_local() && let Some(body_id) = self.infcx.tcx.hir().maybe_body_owned_by(local_def_id) diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index c92fccc959f..304d41d6941 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -26,6 +26,9 @@ use rustc_middle::ty::{self, RegionVid, Ty}; use rustc_middle::ty::{Region, TyCtxt}; use rustc_span::symbol::{kw, Ident}; use rustc_span::Span; +use rustc_trait_selection::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_trait_selection::infer::InferCtxtExt; +use rustc_trait_selection::traits::{Obligation, ObligationCtxt}; use crate::borrowck_errors; use crate::session_diagnostics::{ @@ -810,6 +813,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { self.add_static_impl_trait_suggestion(&mut diag, *fr, fr_name, *outlived_fr); self.suggest_adding_lifetime_params(&mut diag, *fr, *outlived_fr); self.suggest_move_on_borrowing_closure(&mut diag); + self.suggest_deref_closure_value(&mut diag); diag } @@ -1041,6 +1045,147 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { #[allow(rustc::diagnostic_outside_of_impl)] #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable + /// When encountering a lifetime error caused by the return type of a closure, check the + /// corresponding trait bound and see if dereferencing the closure return value would satisfy + /// them. If so, we produce a structured suggestion. + fn suggest_deref_closure_value(&self, diag: &mut Diag<'_>) { + let tcx = self.infcx.tcx; + let map = tcx.hir(); + + // Get the closure return value and type. + let body_id = map.body_owned_by(self.mir_def_id()); + let body = &map.body(body_id); + let value = &body.value.peel_blocks(); + let hir::Node::Expr(closure_expr) = tcx.hir_node_by_def_id(self.mir_def_id()) else { + return; + }; + let fn_call_id = tcx.parent_hir_id(self.mir_hir_id()); + let hir::Node::Expr(expr) = tcx.hir_node(fn_call_id) else { return }; + let def_id = map.enclosing_body_owner(fn_call_id); + let tables = tcx.typeck(def_id); + let Some(return_value_ty) = tables.node_type_opt(value.hir_id) else { return }; + let return_value_ty = self.infcx.resolve_vars_if_possible(return_value_ty); + + // We don't use `ty.peel_refs()` to get the number of `*`s needed to get the root type. + let mut ty = return_value_ty; + let mut count = 0; + while let ty::Ref(_, t, _) = ty.kind() { + ty = *t; + count += 1; + } + if !self.infcx.type_is_copy_modulo_regions(self.param_env, ty) { + return; + } + + // Build a new closure where the return type is an owned value, instead of a ref. + let Some(ty::Closure(did, args)) = + tables.node_type_opt(closure_expr.hir_id).as_ref().map(|ty| ty.kind()) + else { + return; + }; + let sig = args.as_closure().sig(); + let closure_sig_as_fn_ptr_ty = Ty::new_fn_ptr( + tcx, + sig.map_bound(|s| { + let unsafety = hir::Unsafety::Normal; + use rustc_target::spec::abi; + tcx.mk_fn_sig( + [s.inputs()[0]], + s.output().peel_refs(), + s.c_variadic, + unsafety, + abi::Abi::Rust, + ) + }), + ); + let parent_args = GenericArgs::identity_for_item( + tcx, + tcx.typeck_root_def_id(self.mir_def_id().to_def_id()), + ); + let closure_kind = args.as_closure().kind(); + let closure_kind_ty = Ty::from_closure_kind(tcx, closure_kind); + let tupled_upvars_ty = self.infcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::ClosureSynthetic, + span: closure_expr.span, + }); + let closure_args = ty::ClosureArgs::new( + tcx, + ty::ClosureArgsParts { + parent_args, + closure_kind_ty, + closure_sig_as_fn_ptr_ty, + tupled_upvars_ty, + }, + ); + let closure_ty = Ty::new_closure(tcx, *did, closure_args.args); + let closure_ty = tcx.erase_regions(closure_ty); + + let hir::ExprKind::MethodCall(_, rcvr, args, _) = expr.kind else { return }; + let Some(pos) = args + .iter() + .enumerate() + .find(|(_, arg)| arg.hir_id == closure_expr.hir_id) + .map(|(i, _)| i) + else { + return; + }; + // The found `Self` type of the method call. + let Some(possible_rcvr_ty) = tables.node_type_opt(rcvr.hir_id) else { return }; + + // The `MethodCall` expression is `Res::Err`, so we search for the method on the `rcvr_ty`. + let Some(method) = tcx.lookup_method_for_diagnostic((self.mir_def_id(), expr.hir_id)) + else { + return; + }; + + // Get the type for the parameter corresponding to the argument the closure with the + // lifetime error we had. + let Some(input) = tcx + .fn_sig(method) + .instantiate_identity() + .inputs() + .skip_binder() + // Methods have a `self` arg, so `pos` is actually `+ 1` to match the method call arg. + .get(pos + 1) + else { + return; + }; + + trace!(?input); + + let ty::Param(closure_param) = input.kind() else { return }; + + // Get the arguments for the found method, only specifying that `Self` is the receiver type. + let args = GenericArgs::for_item(tcx, method, |param, _| { + if param.index == 0 { + possible_rcvr_ty.into() + } else if param.index == closure_param.index { + closure_ty.into() + } else { + self.infcx.var_for_def(expr.span, param) + } + }); + + let preds = tcx.predicates_of(method).instantiate(tcx, args); + + let ocx = ObligationCtxt::new(&self.infcx); + ocx.register_obligations(preds.iter().map(|(pred, span)| { + trace!(?pred); + Obligation::misc(tcx, span, self.mir_def_id(), self.param_env, pred) + })); + + if ocx.select_all_or_error().is_empty() { + diag.span_suggestion_verbose( + value.span.shrink_to_lo(), + "dereference the return value", + "*".repeat(count), + Applicability::MachineApplicable, + ); + } + } + + #[allow(rustc::diagnostic_outside_of_impl)] + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn suggest_move_on_borrowing_closure(&self, diag: &mut Diag<'_>) { let map = self.infcx.tcx.hir(); let body_id = map.body_owned_by(self.mir_def_id()); diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs index 61a6361ae8d..c79ae716806 100644 --- a/compiler/rustc_builtin_macros/src/source_util.rs +++ b/compiler/rustc_builtin_macros/src/source_util.rs @@ -196,10 +196,10 @@ pub fn expand_include_str( Err(guar) => return ExpandResult::Ready(DummyResult::any(sp, guar)), }; ExpandResult::Ready(match load_binary_file(cx, path.as_str().as_ref(), sp, path_span) { - Ok(bytes) => match std::str::from_utf8(&bytes) { + Ok((bytes, bsp)) => match std::str::from_utf8(&bytes) { Ok(src) => { let interned_src = Symbol::intern(src); - MacEager::expr(cx.expr_str(sp, interned_src)) + MacEager::expr(cx.expr_str(cx.with_def_site_ctxt(bsp), interned_src)) } Err(_) => { let guar = cx.dcx().span_err(sp, format!("`{path}` wasn't a utf-8 file")); @@ -225,7 +225,9 @@ pub fn expand_include_bytes( Err(guar) => return ExpandResult::Ready(DummyResult::any(sp, guar)), }; ExpandResult::Ready(match load_binary_file(cx, path.as_str().as_ref(), sp, path_span) { - Ok(bytes) => { + Ok((bytes, _bsp)) => { + // Don't care about getting the span for the raw bytes, + // because the console can't really show them anyway. let expr = cx.expr(sp, ast::ExprKind::IncludedBytes(bytes)); MacEager::expr(expr) } @@ -238,7 +240,7 @@ fn load_binary_file( original_path: &Path, macro_span: Span, path_span: Span, -) -> Result<Lrc<[u8]>, Box<dyn MacResult>> { +) -> Result<(Lrc<[u8]>, Span), Box<dyn MacResult>> { let resolved_path = match resolve_path(&cx.sess, original_path, macro_span) { Ok(path) => path, Err(err) => { @@ -333,10 +335,8 @@ fn find_path_suggestion( .flatten() .take(4); - for new_path in root_absolute.chain(add).chain(remove) { - if source_map.file_exists(&base_dir.join(&new_path)) { - return Some(new_path); - } - } - None + root_absolute + .chain(add) + .chain(remove) + .find(|new_path| source_map.file_exists(&base_dir.join(&new_path))) } diff --git a/compiler/rustc_codegen_cranelift/.cirrus.yml b/compiler/rustc_codegen_cranelift/.cirrus.yml index 97c2f45d31e..5a464bfac36 100644 --- a/compiler/rustc_codegen_cranelift/.cirrus.yml +++ b/compiler/rustc_codegen_cranelift/.cirrus.yml @@ -7,7 +7,7 @@ task: - curl https://sh.rustup.rs -sSf --output rustup.sh - sh rustup.sh --default-toolchain none -y --profile=minimal target_cache: - folder: target + folder: build/cg_clif prepare_script: - . $HOME/.cargo/env - ./y.sh prepare @@ -16,4 +16,5 @@ task: # Disabling incr comp reduces cache size and incr comp doesn't save as much # on CI anyway. - export CARGO_BUILD_INCREMENTAL=false - - ./y.sh test + # Skip rand as it fails on FreeBSD due to rust-random/rand#1355 + - ./y.sh test --skip-test test.rust-random/rand diff --git a/compiler/rustc_codegen_cranelift/.github/actions/github-release/main.js b/compiler/rustc_codegen_cranelift/.github/actions/github-release/main.js index 6fcfca34ea7..1eb2b7f23b2 100644 --- a/compiler/rustc_codegen_cranelift/.github/actions/github-release/main.js +++ b/compiler/rustc_codegen_cranelift/.github/actions/github-release/main.js @@ -56,7 +56,7 @@ async function runOnce() { force: true, }); } catch (e) { - console.log("ERROR: ", JSON.stringify(e.data, null, 2)); + console.log("ERROR: ", JSON.stringify(e.response, null, 2)); core.info(`creating dev tag`); try { await octokit.rest.git.createRef({ @@ -68,7 +68,7 @@ async function runOnce() { } catch (e) { // we might race with others, so assume someone else has created the // tag by this point. - console.log("failed to create tag: ", JSON.stringify(e.data, null, 2)); + console.log("failed to create tag: ", JSON.stringify(e.response, null, 2)); } } diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/main.yml b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml index 913a5c5a850..14aa850ff5c 100644 --- a/compiler/rustc_codegen_cranelift/.github/workflows/main.yml +++ b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml @@ -268,6 +268,9 @@ jobs: if: ${{ github.ref == 'refs/heads/master' }} needs: [rustfmt, test, bench, dist] + permissions: + contents: write # for creating the dev tag and release + concurrency: group: release-dev cancel-in-progress: true diff --git a/compiler/rustc_codegen_cranelift/patches/0027-stdlib-128bit-atomic-operations.patch b/compiler/rustc_codegen_cranelift/patches/0027-stdlib-128bit-atomic-operations.patch index 646928893e9..a3f370af916 100644 --- a/compiler/rustc_codegen_cranelift/patches/0027-stdlib-128bit-atomic-operations.patch +++ b/compiler/rustc_codegen_cranelift/patches/0027-stdlib-128bit-atomic-operations.patch @@ -82,6 +82,19 @@ index d9de37e..8293fce 100644 #[cfg(target_has_atomic_load_store = "ptr")] macro_rules! atomic_int_ptr_sized { ( $($target_pointer_width:literal $align:literal)* ) => { $( +diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs +index 58b9ba4..91bbd0a 100644 +--- a/library/core/src/cell.rs ++++ b/library/core/src/cell.rs +@@ -2246,8 +2246,6 @@ unsafe_cell_primitive_into_inner! { + u32 "32" + i64 "64" + u64 "64" +- i128 "128" +- u128 "128" + isize "ptr" + usize "ptr" + } -- 2.26.2.7.g19db9cfb68 diff --git a/compiler/rustc_codegen_cranelift/rust-toolchain b/compiler/rustc_codegen_cranelift/rust-toolchain index 09e436b3eed..3e7da4e161f 100644 --- a/compiler/rustc_codegen_cranelift/rust-toolchain +++ b/compiler/rustc_codegen_cranelift/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2024-04-05" +channel = "nightly-2024-04-11" components = ["rust-src", "rustc-dev", "llvm-tools"] diff --git a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh index f42a008dc0c..8580f4557e8 100755 --- a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh +++ b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh @@ -10,14 +10,13 @@ pushd rust command -v rg >/dev/null 2>&1 || cargo install ripgrep -# FIXME remove this workaround once ICE tests no longer emit an outdated nightly message -for test in $(rg -i --files-with-matches "//@(\[.*\])? failure-status: 101" tests/ui); do - echo "rm $test" +rm -r tests/ui/{unsized-locals/,lto/,linkage*} || true +for test in $(rg --files-with-matches "lto" tests/{codegen-units,ui,incremental}); do rm $test done -rm -r tests/ui/{unsized-locals/,lto/,linkage*} || true -for test in $(rg --files-with-matches "lto" tests/{codegen-units,ui,incremental}); do +# should-fail tests don't work when compiletest is compiled with panic=abort +for test in $(rg --files-with-matches "//@ should-fail" tests/{codegen-units,ui,incremental}); do rm $test done @@ -79,7 +78,6 @@ rm -r tests/run-make/fmt-write-bloat/ # tests an optimization # ====================== rm tests/incremental/thinlto/cgu_invalidated_when_import_{added,removed}.rs # requires LLVM rm -r tests/run-make/cross-lang-lto # same -rm -r tests/run-make/issue-7349 # same rm -r tests/run-make/sepcomp-inlining # same rm -r tests/run-make/sepcomp-separate # same rm -r tests/run-make/sepcomp-cci-copies # same @@ -116,8 +114,6 @@ rm -r tests/run-make/compiler-builtins # Expects lib/rustlib/src/rust to contain # genuine bugs # ============ -rm tests/incremental/spike-neg1.rs # errors out for some reason -rm tests/incremental/spike-neg2.rs # same rm -r tests/run-make/extern-fn-explicit-align # argument alignment not yet supported rm -r tests/run-make/panic-abort-eh_frame # .eh_frame emitted with panic=abort diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 771e5b21958..f07421431da 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -267,10 +267,19 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { .generic_activity("codegen prelude") .run(|| crate::abi::codegen_fn_prelude(fx, start_block)); - for (bb, bb_data) in traversal::mono_reachable(fx.mir, fx.tcx, fx.instance) { + let reachable_blocks = traversal::mono_reachable_as_bitset(fx.mir, fx.tcx, fx.instance); + + for (bb, bb_data) in fx.mir.basic_blocks.iter_enumerated() { let block = fx.get_block(bb); fx.bcx.switch_to_block(block); + if !reachable_blocks.contains(bb) { + // We want to skip this block, because it's not reachable. But we still create + // the block so terminators in other blocks can reference it. + fx.bcx.ins().trap(TrapCode::UnreachableCodeReached); + continue; + } + if bb_data.is_cleanup { // Unwinding after panicking is not supported continue; diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs index 96ab7a29205..eebd181341d 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs @@ -38,6 +38,14 @@ impl UnwindContext { } pub(crate) fn add_function(&mut self, func_id: FuncId, context: &Context, isa: &dyn TargetIsa) { + if let target_lexicon::OperatingSystem::MacOSX { .. } = isa.triple().operating_system { + // The object crate doesn't currently support DW_GNU_EH_PE_absptr, which macOS + // requires for unwinding tables. In addition on arm64 it currently doesn't + // support 32bit relocations as we currently use for the unwinding table. + // See gimli-rs/object#415 and rust-lang/rustc_codegen_cranelift#1371 + return; + } + let unwind_info = if let Some(unwind_info) = context.compiled_code().unwrap().create_unwind_info(isa).unwrap() { diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index 43cc46cfe68..6253816d37d 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -974,7 +974,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { &mut self, place: PlaceRef<'tcx, RValue<'gcc>>, ) -> OperandRef<'tcx, RValue<'gcc>> { - assert_eq!(place.llextra.is_some(), place.layout.is_unsized()); + assert_eq!(place.val.llextra.is_some(), place.layout.is_unsized()); if place.layout.is_zst() { return OperandRef::zero_sized(place.layout); @@ -999,10 +999,11 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { } } - let val = if let Some(llextra) = place.llextra { - OperandValue::Ref(place.llval, Some(llextra), place.align) + let val = if let Some(_) = place.val.llextra { + // FIXME: Merge with the `else` below? + OperandValue::Ref(place.val) } else if place.layout.is_gcc_immediate() { - let load = self.load(place.layout.gcc_type(self), place.llval, place.align); + let load = self.load(place.layout.gcc_type(self), place.val.llval, place.val.align); if let abi::Abi::Scalar(ref scalar) = place.layout.abi { scalar_load_metadata(self, load, scalar); } @@ -1012,9 +1013,9 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { let mut load = |i, scalar: &abi::Scalar, align| { let llptr = if i == 0 { - place.llval + place.val.llval } else { - self.inbounds_ptradd(place.llval, self.const_usize(b_offset.bytes())) + self.inbounds_ptradd(place.val.llval, self.const_usize(b_offset.bytes())) }; let llty = place.layout.scalar_pair_element_gcc_type(self, i); let load = self.load(llty, llptr, align); @@ -1027,11 +1028,11 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { }; OperandValue::Pair( - load(0, a, place.align), - load(1, b, place.align.restrict_for_offset(b_offset)), + load(0, a, place.val.align), + load(1, b, place.val.align.restrict_for_offset(b_offset)), ) } else { - OperandValue::Ref(place.llval, None, place.align) + OperandValue::Ref(place.val) }; OperandRef { val, layout: place.layout } @@ -1045,8 +1046,8 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { ) { let zero = self.const_usize(0); let count = self.const_usize(count); - let start = dest.project_index(self, zero).llval; - let end = dest.project_index(self, count).llval; + let start = dest.project_index(self, zero).val.llval; + let end = dest.project_index(self, count).val.llval; let header_bb = self.append_sibling_block("repeat_loop_header"); let body_bb = self.append_sibling_block("repeat_loop_body"); @@ -1064,7 +1065,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { self.cond_br(keep_going, body_bb, next_bb); self.switch_to_block(body_bb); - let align = dest.align.restrict_for_offset(dest.layout.field(self.cx(), 0).size); + let align = dest.val.align.restrict_for_offset(dest.layout.field(self.cx(), 0).size); cg_elem.val.store(self, PlaceRef::new_sized_aligned(current_val, cg_elem.layout, align)); let next = self.inbounds_gep( diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index cebd45c09aa..bee6bda007c 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -11,7 +11,7 @@ use rustc_codegen_ssa::base::wants_msvc_seh; use rustc_codegen_ssa::common::IntPredicate; use rustc_codegen_ssa::errors::InvalidMonomorphization; use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue}; -use rustc_codegen_ssa::mir::place::PlaceRef; +use rustc_codegen_ssa::mir::place::{PlaceRef, PlaceValue}; use rustc_codegen_ssa::traits::{ ArgAbiMethods, BuilderMethods, ConstMethods, IntrinsicCallMethods, }; @@ -354,7 +354,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { let block = self.llbb(); let extended_asm = block.add_extended_asm(None, ""); - extended_asm.add_input_operand(None, "r", result.llval); + extended_asm.add_input_operand(None, "r", result.val.llval); extended_asm.add_clobber("memory"); extended_asm.set_volatile_flag(true); @@ -388,8 +388,8 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { if !fn_abi.ret.is_ignore() { if let PassMode::Cast { cast: ty, .. } = &fn_abi.ret.mode { let ptr_llty = self.type_ptr_to(ty.gcc_type(self)); - let ptr = self.pointercast(result.llval, ptr_llty); - self.store(llval, ptr, result.align); + let ptr = self.pointercast(result.val.llval, ptr_llty); + self.store(llval, ptr, result.val.align); } else { OperandRef::from_immediate_or_packed_pair(self, llval, result.layout) .val @@ -502,7 +502,7 @@ impl<'gcc, 'tcx> ArgAbiExt<'gcc, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> { return; } if self.is_sized_indirect() { - OperandValue::Ref(val, None, self.layout.align.abi).store(bx, dst) + OperandValue::Ref(PlaceValue::new_sized(val, self.layout.align.abi)).store(bx, dst) } else if self.is_unsized_indirect() { bug!("unsized `ArgAbi` must be handled through `store_fn_arg`"); } else if let PassMode::Cast { ref cast, .. } = self.mode { @@ -511,7 +511,7 @@ impl<'gcc, 'tcx> ArgAbiExt<'gcc, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> { let can_store_through_cast_ptr = false; if can_store_through_cast_ptr { let cast_ptr_llty = bx.type_ptr_to(cast.gcc_type(bx)); - let cast_dst = bx.pointercast(dst.llval, cast_ptr_llty); + let cast_dst = bx.pointercast(dst.val.llval, cast_ptr_llty); bx.store(val, cast_dst, self.layout.align.abi); } else { // The actual return type is a struct, but the ABI @@ -539,7 +539,7 @@ impl<'gcc, 'tcx> ArgAbiExt<'gcc, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> { // ... and then memcpy it to the intended destination. bx.memcpy( - dst.llval, + dst.val.llval, self.layout.align.abi, llscratch, scratch_align, @@ -571,7 +571,12 @@ impl<'gcc, 'tcx> ArgAbiExt<'gcc, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> { OperandValue::Pair(next(), next()).store(bx, dst); } PassMode::Indirect { meta_attrs: Some(_), .. } => { - OperandValue::Ref(next(), Some(next()), self.layout.align.abi).store(bx, dst); + let place_val = PlaceValue { + llval: next(), + llextra: Some(next()), + align: self.layout.align.abi, + }; + OperandValue::Ref(place_val).store(bx, dst); } PassMode::Direct(_) | PassMode::Indirect { meta_attrs: None, .. } diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs index 60361a44c2d..6039a4aaf01 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs @@ -82,7 +82,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( let place = PlaceRef::alloca(bx, args[0].layout); args[0].val.store(bx, place); let int_ty = bx.type_ix(expected_bytes * 8); - let ptr = bx.pointercast(place.llval, bx.cx.type_ptr_to(int_ty)); + let ptr = bx.pointercast(place.val.llval, bx.cx.type_ptr_to(int_ty)); bx.load(int_ty, ptr, Align::ONE) } _ => return_error!(InvalidMonomorphization::InvalidBitmask { diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index f918facc86d..c0b43b77897 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -7,7 +7,7 @@ use crate::type_of::LayoutLlvmExt; use crate::value::Value; use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue}; -use rustc_codegen_ssa::mir::place::PlaceRef; +use rustc_codegen_ssa::mir::place::{PlaceRef, PlaceValue}; use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::MemFlags; use rustc_middle::bug; @@ -207,7 +207,7 @@ impl<'ll, 'tcx> ArgAbiExt<'ll, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> { // Sized indirect arguments PassMode::Indirect { attrs, meta_attrs: None, on_stack: _ } => { let align = attrs.pointee_align.unwrap_or(self.layout.align.abi); - OperandValue::Ref(val, None, align).store(bx, dst); + OperandValue::Ref(PlaceValue::new_sized(val, align)).store(bx, dst); } // Unsized indirect qrguments PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => { @@ -233,7 +233,7 @@ impl<'ll, 'tcx> ArgAbiExt<'ll, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> { bx.store(val, llscratch, scratch_align); // ... and then memcpy it to the intended destination. bx.memcpy( - dst.llval, + dst.val.llval, self.layout.align.abi, llscratch, scratch_align, @@ -265,7 +265,12 @@ impl<'ll, 'tcx> ArgAbiExt<'ll, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> { OperandValue::Pair(next(), next()).store(bx, dst); } PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => { - OperandValue::Ref(next(), Some(next()), self.layout.align.abi).store(bx, dst); + let place_val = PlaceValue { + llval: next(), + llextra: Some(next()), + align: self.layout.align.abi, + }; + OperandValue::Ref(place_val).store(bx, dst); } PassMode::Direct(_) | PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ } diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index 500904ce188..e09869cf425 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -220,7 +220,7 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { constraints.append(&mut clobbers); if !options.contains(InlineAsmOptions::PRESERVES_FLAGS) { match asm_arch { - InlineAsmArch::AArch64 | InlineAsmArch::Arm => { + InlineAsmArch::AArch64 | InlineAsmArch::Arm64EC | InlineAsmArch::Arm => { constraints.push("~{cc}".to_string()); } InlineAsmArch::X86 | InlineAsmArch::X86_64 => { diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index b7235972204..06d9be1869c 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -535,7 +535,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { panic!("unsized locals must not be `extern` types"); } } - assert_eq!(place.llextra.is_some(), place.layout.is_unsized()); + assert_eq!(place.val.llextra.is_some(), place.layout.is_unsized()); if place.layout.is_zst() { return OperandRef::zero_sized(place.layout); @@ -579,13 +579,14 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } } - let val = if let Some(llextra) = place.llextra { - OperandValue::Ref(place.llval, Some(llextra), place.align) + let val = if let Some(_) = place.val.llextra { + // FIXME: Merge with the `else` below? + OperandValue::Ref(place.val) } else if place.layout.is_llvm_immediate() { let mut const_llval = None; let llty = place.layout.llvm_type(self); unsafe { - if let Some(global) = llvm::LLVMIsAGlobalVariable(place.llval) { + if let Some(global) = llvm::LLVMIsAGlobalVariable(place.val.llval) { if llvm::LLVMIsGlobalConstant(global) == llvm::True { if let Some(init) = llvm::LLVMGetInitializer(global) { if self.val_ty(init) == llty { @@ -596,7 +597,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } } let llval = const_llval.unwrap_or_else(|| { - let load = self.load(llty, place.llval, place.align); + let load = self.load(llty, place.val.llval, place.val.align); if let abi::Abi::Scalar(scalar) = place.layout.abi { scalar_load_metadata(self, load, scalar, place.layout, Size::ZERO); } @@ -608,9 +609,9 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { let mut load = |i, scalar: abi::Scalar, layout, align, offset| { let llptr = if i == 0 { - place.llval + place.val.llval } else { - self.inbounds_ptradd(place.llval, self.const_usize(b_offset.bytes())) + self.inbounds_ptradd(place.val.llval, self.const_usize(b_offset.bytes())) }; let llty = place.layout.scalar_pair_element_llvm_type(self, i, false); let load = self.load(llty, llptr, align); @@ -619,11 +620,11 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { }; OperandValue::Pair( - load(0, a, place.layout, place.align, Size::ZERO), - load(1, b, place.layout, place.align.restrict_for_offset(b_offset), b_offset), + load(0, a, place.layout, place.val.align, Size::ZERO), + load(1, b, place.layout, place.val.align.restrict_for_offset(b_offset), b_offset), ) } else { - OperandValue::Ref(place.llval, None, place.align) + OperandValue::Ref(place.val) }; OperandRef { val, layout: place.layout } diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index dc52dd156b7..2bed7c1bd1c 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -264,7 +264,7 @@ impl<'ll, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'_, 'll, 'tcx> { llvm::LLVMSetAlignment(load, align); } if !result.layout.is_zst() { - self.store(load, result.llval, result.align); + self.store_to_place(load, result.val); } return Ok(()); } @@ -428,7 +428,7 @@ impl<'ll, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'_, 'll, 'tcx> { sym::black_box => { args[0].val.store(self, result); - let result_val_span = [result.llval]; + let result_val_span = [result.val.llval]; // We need to "use" the argument in some way LLVM can't introspect, and on // targets that support it we can typically leverage inline assembly to do // this. LLVM's interpretation of inline assembly is that it's, well, a black @@ -482,7 +482,7 @@ impl<'ll, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'_, 'll, 'tcx> { if !fn_abi.ret.is_ignore() { if let PassMode::Cast { .. } = &fn_abi.ret.mode { - self.store(llval, result.llval, result.align); + self.store(llval, result.val.llval, result.val.align); } else { OperandRef::from_immediate_or_packed_pair(self, llval, result.layout) .val @@ -1065,7 +1065,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( let place = PlaceRef::alloca(bx, args[0].layout); args[0].val.store(bx, place); let int_ty = bx.type_ix(expected_bytes * 8); - bx.load(int_ty, place.llval, Align::ONE) + bx.load(int_ty, place.val.llval, Align::ONE) } _ => return_error!(InvalidMonomorphization::InvalidBitmask { span, diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index ac278de02af..e7d6a671e12 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -11,6 +11,7 @@ use rustc_metadata::fs::{copy_to_stdout, emit_wrapper_file, METADATA_FILENAME}; use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::middle::exported_symbols::SymbolExportKind; +use rustc_session::config::LinkerFeaturesCli; use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, OutFileName, Strip}; use rustc_session::config::{OutputFilenames, OutputType, PrintKind, SplitDwarfKind}; use rustc_session::cstore::DllImport; @@ -22,10 +23,10 @@ use rustc_session::utils::NativeLibKind; use rustc_session::{filesearch, Session}; use rustc_span::symbol::Symbol; use rustc_target::spec::crt_objects::CrtObjects; -use rustc_target::spec::LinkSelfContainedComponents; use rustc_target::spec::LinkSelfContainedDefault; use rustc_target::spec::LinkerFlavorCli; use rustc_target::spec::{Cc, LinkOutputKind, LinkerFlavor, Lld, PanicStrategy}; +use rustc_target::spec::{LinkSelfContainedComponents, LinkerFeatures}; use rustc_target::spec::{RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo}; use super::archive::{ArchiveBuilder, ArchiveBuilderBuilder}; @@ -42,7 +43,6 @@ use regex::Regex; use tempfile::Builder as TempFileBuilder; use itertools::Itertools; -use std::cell::OnceCell; use std::collections::BTreeSet; use std::ffi::{OsStr, OsString}; use std::fs::{read, File, OpenOptions}; @@ -52,15 +52,6 @@ use std::path::{Path, PathBuf}; use std::process::{ExitStatus, Output, Stdio}; use std::{env, fmt, fs, io, mem, str}; -#[derive(Default)] -pub struct SearchPaths(OnceCell<Vec<PathBuf>>); - -impl SearchPaths { - pub(super) fn get(&self, sess: &Session) -> &[PathBuf] { - self.0.get_or_init(|| archive_search_paths(sess)) - } -} - pub fn ensure_removed(dcx: &DiagCtxt, path: &Path) { if let Err(e) = fs::remove_file(path) { if e.kind() != io::ErrorKind::NotFound { @@ -71,8 +62,8 @@ pub fn ensure_removed(dcx: &DiagCtxt, path: &Path) { /// Performs the linkage portion of the compilation phase. This will generate all /// of the requested outputs for this compilation session. -pub fn link_binary<'a>( - sess: &'a Session, +pub fn link_binary( + sess: &Session, archive_builder_builder: &dyn ArchiveBuilderBuilder, codegen_results: &CodegenResults, outputs: &OutputFilenames, @@ -310,8 +301,6 @@ fn link_rlib<'a>( flavor: RlibFlavor, tmpdir: &MaybeTempDir, ) -> Result<Box<dyn ArchiveBuilder + 'a>, ErrorGuaranteed> { - let lib_search_paths = archive_search_paths(sess); - let mut ab = archive_builder_builder.new_archive_builder(sess); let trailing_metadata = match flavor { @@ -385,19 +374,14 @@ fn link_rlib<'a>( if flavor == RlibFlavor::Normal && let Some(filename) = lib.filename { - let path = find_native_static_library(filename.as_str(), true, &lib_search_paths, sess); + let path = find_native_static_library(filename.as_str(), true, sess); let src = read(path) .map_err(|e| sess.dcx().emit_fatal(errors::ReadFileError { message: e }))?; let (data, _) = create_wrapper_file(sess, ".bundled_lib".to_string(), &src); let wrapper_file = emit_wrapper_file(sess, &data, tmpdir, filename.as_str()); packed_bundled_libs.push(wrapper_file); } else { - let path = find_native_static_library( - lib.name.as_str(), - lib.verbatim, - &lib_search_paths, - sess, - ); + let path = find_native_static_library(lib.name.as_str(), lib.verbatim, sess); ab.add_archive(&path, Box::new(|_| false)).unwrap_or_else(|error| { sess.dcx().emit_fatal(errors::AddNativeLibrary { library_path: path, error }) }); @@ -464,9 +448,9 @@ fn link_rlib<'a>( /// then the CodegenResults value contains one NativeLib instance for each block. However, the /// linker appears to expect only a single import library for each library used, so we need to /// collate the symbols together by library name before generating the import libraries. -fn collate_raw_dylibs<'a, 'b>( - sess: &'a Session, - used_libraries: impl IntoIterator<Item = &'b NativeLib>, +fn collate_raw_dylibs<'a>( + sess: &Session, + used_libraries: impl IntoIterator<Item = &'a NativeLib>, ) -> Result<Vec<(String, Vec<DllImport>)>, ErrorGuaranteed> { // Use index maps to preserve original order of imports and libraries. let mut dylib_table = FxIndexMap::<String, FxIndexMap<Symbol, &DllImport>>::default(); @@ -513,8 +497,8 @@ fn collate_raw_dylibs<'a, 'b>( /// /// There's no need to include metadata in a static archive, so ensure to not link in the metadata /// object file (and also don't prepare the archive with a metadata file). -fn link_staticlib<'a>( - sess: &'a Session, +fn link_staticlib( + sess: &Session, archive_builder_builder: &dyn ArchiveBuilderBuilder, codegen_results: &CodegenResults, out_filename: &Path, @@ -626,11 +610,7 @@ fn link_staticlib<'a>( /// Use `thorin` (rust implementation of a dwarf packaging utility) to link DWARF objects into a /// DWARF package. -fn link_dwarf_object<'a>( - sess: &'a Session, - cg_results: &CodegenResults, - executable_out_filename: &Path, -) { +fn link_dwarf_object(sess: &Session, cg_results: &CodegenResults, executable_out_filename: &Path) { let mut dwp_out_filename = executable_out_filename.to_path_buf().into_os_string(); dwp_out_filename.push(".dwp"); debug!(?dwp_out_filename, ?executable_out_filename); @@ -734,8 +714,8 @@ fn link_dwarf_object<'a>( /// /// This will invoke the system linker/cc to create the resulting file. This links to all upstream /// files as well. -fn link_natively<'a>( - sess: &'a Session, +fn link_natively( + sess: &Session, archive_builder_builder: &dyn ArchiveBuilderBuilder, crate_type: CrateType, out_filename: &Path, @@ -1099,8 +1079,8 @@ fn link_natively<'a>( Ok(()) } -fn strip_symbols_with_external_utility<'a>( - sess: &'a Session, +fn strip_symbols_with_external_utility( + sess: &Session, util: &str, out_filename: &Path, option: Option<&str>, @@ -1337,7 +1317,9 @@ pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) { sess: &Session, linker: Option<PathBuf>, flavor: Option<LinkerFlavor>, + features: LinkerFeaturesCli, ) -> Option<(PathBuf, LinkerFlavor)> { + let flavor = flavor.map(|flavor| adjust_flavor_to_features(flavor, features)); match (linker, flavor) { (Some(linker), Some(flavor)) => Some((linker, flavor)), // only the linker flavor is known; use the default linker for the selected flavor @@ -1385,12 +1367,33 @@ pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) { sess.dcx().emit_fatal(errors::LinkerFileStem); }); let flavor = sess.target.linker_flavor.with_linker_hints(stem); + let flavor = adjust_flavor_to_features(flavor, features); Some((linker, flavor)) } (None, None) => None, } } + // While linker flavors and linker features are isomorphic (and thus targets don't need to + // define features separately), we use the flavor as the root piece of data and have the + // linker-features CLI flag influence *that*, so that downstream code does not have to check for + // both yet. + fn adjust_flavor_to_features( + flavor: LinkerFlavor, + features: LinkerFeaturesCli, + ) -> LinkerFlavor { + // Note: a linker feature cannot be both enabled and disabled on the CLI. + if features.enabled.contains(LinkerFeatures::LLD) { + flavor.with_lld_enabled() + } else if features.disabled.contains(LinkerFeatures::LLD) { + flavor.with_lld_disabled() + } else { + flavor + } + } + + let features = sess.opts.unstable_opts.linker_features; + // linker and linker flavor specified via command line have precedence over what the target // specification specifies let linker_flavor = match sess.opts.cg.linker_flavor { @@ -1404,7 +1407,7 @@ pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) { .linker_flavor .map(|flavor| sess.target.linker_flavor.with_cli_hints(flavor)), }; - if let Some(ret) = infer_from(sess, sess.opts.cg.linker.clone(), linker_flavor) { + if let Some(ret) = infer_from(sess, sess.opts.cg.linker.clone(), linker_flavor, features) { return ret; } @@ -1412,6 +1415,7 @@ pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) { sess, sess.target.linker.as_deref().map(PathBuf::from), Some(sess.target.linker_flavor), + features, ) { return ret; } @@ -1445,10 +1449,6 @@ fn preserve_objects_for_their_debuginfo(sess: &Session) -> (bool, bool) { } } -fn archive_search_paths(sess: &Session) -> Vec<PathBuf> { - sess.target_filesearch(PathKind::Native).search_path_dirs() -} - #[derive(PartialEq)] enum RlibFlavor { Normal, @@ -2108,10 +2108,10 @@ fn add_rpath_args( /// to the linking process as a whole. /// Order-independent options may still override each other in order-dependent fashion, /// e.g `--foo=yes --foo=no` may be equivalent to `--foo=no`. -fn linker_with_args<'a>( +fn linker_with_args( path: &Path, flavor: LinkerFlavor, - sess: &'a Session, + sess: &Session, archive_builder_builder: &dyn ArchiveBuilderBuilder, crate_type: CrateType, tmpdir: &Path, @@ -2500,7 +2500,6 @@ fn add_native_libs_from_crate( archive_builder_builder: &dyn ArchiveBuilderBuilder, codegen_results: &CodegenResults, tmpdir: &Path, - search_paths: &SearchPaths, bundled_libs: &FxIndexSet<Symbol>, cnum: CrateNum, link_static: bool, @@ -2563,7 +2562,7 @@ fn add_native_libs_from_crate( cmd.link_staticlib_by_path(&path, whole_archive); } } else { - cmd.link_staticlib_by_name(name, verbatim, whole_archive, search_paths); + cmd.link_staticlib_by_name(name, verbatim, whole_archive); } } } @@ -2577,7 +2576,7 @@ fn add_native_libs_from_crate( // link kind is unspecified. if !link_output_kind.can_link_dylib() && !sess.target.crt_static_allows_dylibs { if link_static { - cmd.link_staticlib_by_name(name, verbatim, false, search_paths); + cmd.link_staticlib_by_name(name, verbatim, false); } } else { if link_dynamic { @@ -2624,7 +2623,6 @@ fn add_local_native_libraries( } } - let search_paths = SearchPaths::default(); // All static and dynamic native library dependencies are linked to the local crate. let link_static = true; let link_dynamic = true; @@ -2634,7 +2632,6 @@ fn add_local_native_libraries( archive_builder_builder, codegen_results, tmpdir, - &search_paths, &Default::default(), LOCAL_CRATE, link_static, @@ -2643,9 +2640,9 @@ fn add_local_native_libraries( ); } -fn add_upstream_rust_crates<'a>( +fn add_upstream_rust_crates( cmd: &mut dyn Linker, - sess: &'a Session, + sess: &Session, archive_builder_builder: &dyn ArchiveBuilderBuilder, codegen_results: &CodegenResults, crate_type: CrateType, @@ -2666,7 +2663,6 @@ fn add_upstream_rust_crates<'a>( .find(|(ty, _)| *ty == crate_type) .expect("failed to find crate type in dependency format list"); - let search_paths = SearchPaths::default(); for &cnum in &codegen_results.crate_info.used_crates { // We may not pass all crates through to the linker. Some crates may appear statically in // an existing dylib, meaning we'll pick up all the symbols from the dylib. @@ -2723,7 +2719,6 @@ fn add_upstream_rust_crates<'a>( archive_builder_builder, codegen_results, tmpdir, - &search_paths, &bundled_libs, cnum, link_static, @@ -2741,7 +2736,6 @@ fn add_upstream_native_libraries( tmpdir: &Path, link_output_kind: LinkOutputKind, ) { - let search_paths = SearchPaths::default(); for &cnum in &codegen_results.crate_info.used_crates { // Static libraries are not linked here, they are linked in `add_upstream_rust_crates`. // FIXME: Merge this function to `add_upstream_rust_crates` so that all native libraries @@ -2763,7 +2757,6 @@ fn add_upstream_native_libraries( archive_builder_builder, codegen_results, tmpdir, - &search_paths, &Default::default(), cnum, link_static, @@ -2782,7 +2775,7 @@ fn add_upstream_native_libraries( // file generated by the MSVC linker. See https://github.com/rust-lang/rust/issues/112586. // // The returned path will always have `fix_windows_verbatim_for_gcc()` applied to it. -fn rehome_sysroot_lib_dir<'a>(sess: &'a Session, lib_dir: &Path) -> PathBuf { +fn rehome_sysroot_lib_dir(sess: &Session, lib_dir: &Path) -> PathBuf { let sysroot_lib_path = sess.target_filesearch(PathKind::All).get_lib_path(); let canonical_sysroot_lib_path = { try_canonicalize(&sysroot_lib_path).unwrap_or_else(|_| sysroot_lib_path.clone()) }; @@ -2815,9 +2808,9 @@ fn rehome_sysroot_lib_dir<'a>(sess: &'a Session, lib_dir: &Path) -> PathBuf { // Note, however, that if we're not doing LTO we can just pass the rlib // blindly to the linker (fast) because it's fine if it's not actually // included as we're at the end of the dependency chain. -fn add_static_crate<'a>( +fn add_static_crate( cmd: &mut dyn Linker, - sess: &'a Session, + sess: &Session, archive_builder_builder: &dyn ArchiveBuilderBuilder, codegen_results: &CodegenResults, tmpdir: &Path, @@ -2990,13 +2983,29 @@ fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) { match flavor { LinkerFlavor::Darwin(Cc::Yes, _) => { - cmd.args(&["-isysroot", &sdk_root, "-Wl,-syslibroot", &sdk_root]); + // Use `-isysroot` instead of `--sysroot`, as only the former + // makes Clang treat it as a platform SDK. + // + // This is admittedly a bit strange, as on most targets + // `-isysroot` only applies to include header files, but on Apple + // targets this also applies to libraries and frameworks. + cmd.args(&["-isysroot", &sdk_root]); } LinkerFlavor::Darwin(Cc::No, _) => { cmd.args(&["-syslibroot", &sdk_root]); } _ => unreachable!(), } + + if llvm_target.contains("macabi") { + // Mac Catalyst uses the macOS SDK, but to link to iOS-specific + // frameworks, we must have the support library stubs in the library + // search path. + + // The flags are called `-L` and `-F` both in Clang, ld64 and ldd. + cmd.arg(format!("-L{sdk_root}/System/iOSSupport/usr/lib")); + cmd.arg(format!("-F{sdk_root}/System/iOSSupport/System/Library/Frameworks")); + } } fn get_apple_sdk_root(sdk_name: &str) -> Result<String, errors::AppleSdkRootError<'_>> { diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index f5640ea26bc..fad6f439441 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -1,6 +1,5 @@ use super::command::Command; use super::symbol_export; -use crate::back::link::SearchPaths; use crate::errors; use rustc_span::symbol::sym; @@ -172,13 +171,7 @@ pub trait Linker { fn link_framework_by_name(&mut self, _name: &str, _verbatim: bool, _as_needed: bool) { bug!("framework linked with unsupported linker") } - fn link_staticlib_by_name( - &mut self, - name: &str, - verbatim: bool, - whole_archive: bool, - search_paths: &SearchPaths, - ); + fn link_staticlib_by_name(&mut self, name: &str, verbatim: bool, whole_archive: bool); fn link_staticlib_by_path(&mut self, path: &Path, whole_archive: bool); fn include_path(&mut self, path: &Path); fn framework_path(&mut self, path: &Path); @@ -482,13 +475,7 @@ impl<'a> Linker for GccLinker<'a> { self.cmd.arg("-framework").arg(name); } - fn link_staticlib_by_name( - &mut self, - name: &str, - verbatim: bool, - whole_archive: bool, - search_paths: &SearchPaths, - ) { + fn link_staticlib_by_name(&mut self, name: &str, verbatim: bool, whole_archive: bool) { self.hint_static(); let colon = if verbatim && self.is_gnu { ":" } else { "" }; if !whole_archive { @@ -497,8 +484,7 @@ impl<'a> Linker for GccLinker<'a> { // -force_load is the macOS equivalent of --whole-archive, but it // involves passing the full path to the library to link. self.linker_arg("-force_load"); - let search_paths = search_paths.get(self.sess); - self.linker_arg(find_native_static_library(name, verbatim, search_paths, self.sess)); + self.linker_arg(find_native_static_library(name, verbatim, self.sess)); } else { self.linker_arg("--whole-archive"); self.cmd.arg(format!("-l{colon}{name}")); @@ -825,13 +811,7 @@ impl<'a> Linker for MsvcLinker<'a> { self.cmd.arg(format!("{}{}", name, if verbatim { "" } else { ".lib" })); } - fn link_staticlib_by_name( - &mut self, - name: &str, - verbatim: bool, - whole_archive: bool, - _search_paths: &SearchPaths, - ) { + fn link_staticlib_by_name(&mut self, name: &str, verbatim: bool, whole_archive: bool) { let prefix = if whole_archive { "/WHOLEARCHIVE:" } else { "" }; let suffix = if verbatim { "" } else { ".lib" }; self.cmd.arg(format!("{prefix}{name}{suffix}")); @@ -1064,13 +1044,7 @@ impl<'a> Linker for EmLinker<'a> { self.cmd.arg("-l").arg(name); } - fn link_staticlib_by_name( - &mut self, - name: &str, - _verbatim: bool, - _whole_archive: bool, - _search_paths: &SearchPaths, - ) { + fn link_staticlib_by_name(&mut self, name: &str, _verbatim: bool, _whole_archive: bool) { self.cmd.arg("-l").arg(name); } @@ -1243,13 +1217,7 @@ impl<'a> Linker for WasmLd<'a> { self.cmd.arg("-l").arg(name); } - fn link_staticlib_by_name( - &mut self, - name: &str, - _verbatim: bool, - whole_archive: bool, - _search_paths: &SearchPaths, - ) { + fn link_staticlib_by_name(&mut self, name: &str, _verbatim: bool, whole_archive: bool) { if !whole_archive { self.cmd.arg("-l").arg(name); } else { @@ -1396,13 +1364,7 @@ impl<'a> Linker for L4Bender<'a> { bug!("dylibs are not supported on L4Re"); } - fn link_staticlib_by_name( - &mut self, - name: &str, - _verbatim: bool, - whole_archive: bool, - _search_paths: &SearchPaths, - ) { + fn link_staticlib_by_name(&mut self, name: &str, _verbatim: bool, whole_archive: bool) { self.hint_static(); if !whole_archive { self.cmd.arg(format!("-PC{name}")); @@ -1580,20 +1542,13 @@ impl<'a> Linker for AixLinker<'a> { self.cmd.arg(format!("-l{name}")); } - fn link_staticlib_by_name( - &mut self, - name: &str, - verbatim: bool, - whole_archive: bool, - search_paths: &SearchPaths, - ) { + fn link_staticlib_by_name(&mut self, name: &str, verbatim: bool, whole_archive: bool) { self.hint_static(); if !whole_archive { self.cmd.arg(format!("-l{name}")); } else { let mut arg = OsString::from("-bkeepfile:"); - let search_path = search_paths.get(self.sess); - arg.push(find_native_static_library(name, verbatim, search_path, self.sess)); + arg.push(find_native_static_library(name, verbatim, self.sess)); self.cmd.arg(arg); } } @@ -1792,13 +1747,7 @@ impl<'a> Linker for PtxLinker<'a> { panic!("external dylibs not supported") } - fn link_staticlib_by_name( - &mut self, - _name: &str, - _verbatim: bool, - _whole_archive: bool, - _search_paths: &SearchPaths, - ) { + fn link_staticlib_by_name(&mut self, _name: &str, _verbatim: bool, _whole_archive: bool) { panic!("staticlibs not supported") } @@ -1880,13 +1829,7 @@ impl<'a> Linker for LlbcLinker<'a> { panic!("external dylibs not supported") } - fn link_staticlib_by_name( - &mut self, - _name: &str, - _verbatim: bool, - _whole_archive: bool, - _search_paths: &SearchPaths, - ) { + fn link_staticlib_by_name(&mut self, _name: &str, _verbatim: bool, _whole_archive: bool) { panic!("staticlibs not supported") } @@ -1977,13 +1920,7 @@ impl<'a> Linker for BpfLinker<'a> { panic!("external dylibs not supported") } - fn link_staticlib_by_name( - &mut self, - _name: &str, - _verbatim: bool, - _whole_archive: bool, - _search_paths: &SearchPaths, - ) { + fn link_staticlib_by_name(&mut self, _name: &str, _verbatim: bool, _whole_archive: bool) { panic!("staticlibs not supported") } diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index c3137f0628e..24f2c50e882 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -1,6 +1,6 @@ use super::operand::OperandRef; use super::operand::OperandValue::{Immediate, Pair, Ref, ZeroSized}; -use super::place::PlaceRef; +use super::place::{PlaceRef, PlaceValue}; use super::{CachedLlbb, FunctionCx, LocalRef}; use crate::base; @@ -242,7 +242,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { bx.switch_to_block(fx.llbb(target)); fx.set_debug_loc(bx, self.terminator.source_info); for tmp in copied_constant_arguments { - bx.lifetime_end(tmp.llval, tmp.layout.size); + bx.lifetime_end(tmp.val.llval, tmp.layout.size); } fx.store_return(bx, ret_dest, &fn_abi.ret, invokeret); } @@ -256,7 +256,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { if let Some((ret_dest, target)) = destination { for tmp in copied_constant_arguments { - bx.lifetime_end(tmp.llval, tmp.layout.size); + bx.lifetime_end(tmp.val.llval, tmp.layout.size); } fx.store_return(bx, ret_dest, &fn_abi.ret, llret); self.funclet_br(fx, bx, target, mergeable_succ) @@ -431,7 +431,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let va_list_arg_idx = self.fn_abi.args.len(); match self.locals[mir::Local::from_usize(1 + va_list_arg_idx)] { LocalRef::Place(va_list) => { - bx.va_end(va_list.llval); + bx.va_end(va_list.val.llval); } _ => bug!("C-variadic function must have a `VaList` place"), } @@ -455,8 +455,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { PassMode::Direct(_) | PassMode::Pair(..) => { let op = self.codegen_consume(bx, mir::Place::return_place().as_ref()); - if let Ref(llval, _, align) = op.val { - bx.load(bx.backend_type(op.layout), llval, align) + if let Ref(place_val) = op.val { + bx.load_from_place(bx.backend_type(op.layout), place_val) } else { op.immediate_or_packed_pair(bx) } @@ -466,21 +466,23 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let op = match self.locals[mir::RETURN_PLACE] { LocalRef::Operand(op) => op, LocalRef::PendingOperand => bug!("use of return before def"), - LocalRef::Place(cg_place) => OperandRef { - val: Ref(cg_place.llval, None, cg_place.align), - layout: cg_place.layout, - }, + LocalRef::Place(cg_place) => { + OperandRef { val: Ref(cg_place.val), layout: cg_place.layout } + } LocalRef::UnsizedPlace(_) => bug!("return type must be sized"), }; let llslot = match op.val { Immediate(_) | Pair(..) => { let scratch = PlaceRef::alloca(bx, self.fn_abi.ret.layout); op.val.store(bx, scratch); - scratch.llval + scratch.val.llval } - Ref(llval, _, align) => { - assert_eq!(align, op.layout.align.abi, "return place is unaligned!"); - llval + Ref(place_val) => { + assert_eq!( + place_val.align, op.layout.align.abi, + "return place is unaligned!" + ); + place_val.llval } ZeroSized => bug!("ZST return value shouldn't be in PassMode::Cast"), }; @@ -512,11 +514,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let place = self.codegen_place(bx, location.as_ref()); let (args1, args2); - let mut args = if let Some(llextra) = place.llextra { - args2 = [place.llval, llextra]; + let mut args = if let Some(llextra) = place.val.llextra { + args2 = [place.val.llval, llextra]; &args2[..] } else { - args1 = [place.llval]; + args1 = [place.val.llval]; &args1[..] }; let (drop_fn, fn_abi, drop_instance) = @@ -918,7 +920,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let dest = match ret_dest { _ if fn_abi.ret.is_indirect() => llargs[0], ReturnDest::Nothing => bx.const_undef(bx.type_ptr()), - ReturnDest::IndirectOperand(dst, _) | ReturnDest::Store(dst) => dst.llval, + ReturnDest::IndirectOperand(dst, _) | ReturnDest::Store(dst) => dst.val.llval, ReturnDest::DirectOperand(_) => { bug!("Cannot use direct operand with an intrinsic call") } @@ -951,7 +953,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { match Self::codegen_intrinsic_call(bx, instance, fn_abi, &args, dest, span) { Ok(()) => { if let ReturnDest::IndirectOperand(dst, _) = ret_dest { - self.store_return(bx, ret_dest, &fn_abi.ret, dst.llval); + self.store_return(bx, ret_dest, &fn_abi.ret, dst.val.llval); } return if let Some(target) = target { @@ -1032,7 +1034,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { llargs.push(data_ptr); continue 'make_args; } - Ref(data_ptr, Some(meta), _) => { + Ref(PlaceValue { llval: data_ptr, llextra: Some(meta), .. }) => { // by-value dynamic dispatch llfn = Some(meth::VirtualIndex::from_index(idx).get_fn( bx, @@ -1058,16 +1060,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { span_bug!(span, "can't codegen a virtual call on {:#?}", op); } let place = op.deref(bx.cx()); - let data_ptr = place.project_field(bx, 0); - let meta_ptr = place.project_field(bx, 1); - let meta = bx.load_operand(meta_ptr); + let data_place = place.project_field(bx, 0); + let meta_place = place.project_field(bx, 1); + let meta = bx.load_operand(meta_place); llfn = Some(meth::VirtualIndex::from_index(idx).get_fn( bx, meta.immediate(), op.layout.ty, fn_abi, )); - llargs.push(data_ptr.llval); + llargs.push(data_place.val.llval); continue; } _ => { @@ -1079,12 +1081,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // The callee needs to own the argument memory if we pass it // by-ref, so make a local copy of non-immediate constants. match (&arg.node, op.val) { - (&mir::Operand::Copy(_), Ref(_, None, _)) - | (&mir::Operand::Constant(_), Ref(_, None, _)) => { + (&mir::Operand::Copy(_), Ref(PlaceValue { llextra: None, .. })) + | (&mir::Operand::Constant(_), Ref(PlaceValue { llextra: None, .. })) => { let tmp = PlaceRef::alloca(bx, op.layout); - bx.lifetime_start(tmp.llval, tmp.layout.size); + bx.lifetime_start(tmp.val.llval, tmp.layout.size); op.val.store(bx, tmp); - op.val = Ref(tmp.llval, None, tmp.align); + op.val = Ref(tmp.val); copied_constant_arguments.push(tmp); } _ => {} @@ -1428,7 +1430,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { _ => bug!("codegen_argument: {:?} invalid for pair argument", op), }, PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => match op.val { - Ref(a, Some(b), _) => { + Ref(PlaceValue { llval: a, llextra: Some(b), .. }) => { llargs.push(a); llargs.push(b); return; @@ -1450,34 +1452,34 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { }; let scratch = PlaceRef::alloca_aligned(bx, arg.layout, required_align); op.val.store(bx, scratch); - (scratch.llval, scratch.align, true) + (scratch.val.llval, scratch.val.align, true) } PassMode::Cast { .. } => { let scratch = PlaceRef::alloca(bx, arg.layout); op.val.store(bx, scratch); - (scratch.llval, scratch.align, true) + (scratch.val.llval, scratch.val.align, true) } _ => (op.immediate_or_packed_pair(bx), arg.layout.align.abi, false), }, - Ref(llval, llextra, align) => match arg.mode { + Ref(op_place_val) => match arg.mode { PassMode::Indirect { attrs, .. } => { let required_align = match attrs.pointee_align { Some(pointee_align) => cmp::max(pointee_align, arg.layout.align.abi), None => arg.layout.align.abi, }; - if align < required_align { + if op_place_val.align < required_align { // For `foo(packed.large_field)`, and types with <4 byte alignment on x86, // alignment requirements may be higher than the type's alignment, so copy // to a higher-aligned alloca. let scratch = PlaceRef::alloca_aligned(bx, arg.layout, required_align); - let op_place = PlaceRef { llval, llextra, layout: op.layout, align }; + let op_place = PlaceRef { val: op_place_val, layout: op.layout }; bx.typed_place_copy(scratch, op_place); - (scratch.llval, scratch.align, true) + (scratch.val.llval, scratch.val.align, true) } else { - (llval, align, true) + (op_place_val.llval, op_place_val.align, true) } } - _ => (llval, align, true), + _ => (op_place_val.llval, op_place_val.align, true), }, ZeroSized => match arg.mode { PassMode::Indirect { on_stack, .. } => { @@ -1490,7 +1492,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // a pointer for `repr(C)` structs even when empty, so get // one from an `alloca` (which can be left uninitialized). let scratch = PlaceRef::alloca(bx, arg.layout); - (scratch.llval, scratch.align, true) + (scratch.val.llval, scratch.val.align, true) } _ => bug!("ZST {op:?} wasn't ignored, but was passed with abi {arg:?}"), }, @@ -1557,15 +1559,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let tuple = self.codegen_operand(bx, operand); // Handle both by-ref and immediate tuples. - if let Ref(llval, None, align) = tuple.val { - let tuple_ptr = PlaceRef::new_sized_aligned(llval, tuple.layout, align); + if let Ref(place_val) = tuple.val { + if place_val.llextra.is_some() { + bug!("closure arguments must be sized"); + } + let tuple_ptr = PlaceRef { val: place_val, layout: tuple.layout }; for i in 0..tuple.layout.fields.count() { let field_ptr = tuple_ptr.project_field(bx, i); let field = bx.load_operand(field_ptr); self.codegen_argument(bx, field, llargs, &args[i]); } - } else if let Ref(_, Some(_), _) = tuple.val { - bug!("closure arguments must be sized") } else { // If the tuple is immediate, the elements are as well. for i in 0..tuple.layout.fields.count() { @@ -1782,7 +1785,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // but the calling convention has an indirect return. let tmp = PlaceRef::alloca(bx, fn_ret.layout); tmp.storage_live(bx); - llargs.push(tmp.llval); + llargs.push(tmp.val.llval); ReturnDest::IndirectOperand(tmp, index) } else if intrinsic.is_some() { // Currently, intrinsics always need a location to store @@ -1803,7 +1806,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { self.codegen_place(bx, mir::PlaceRef { local: dest.local, projection: dest.projection }) }; if fn_ret.is_indirect() { - if dest.align < dest.layout.align.abi { + if dest.val.align < dest.layout.align.abi { // Currently, MIR code generation does not create calls // that store directly to fields of packed structs (in // fact, the calls it creates write only to temps). @@ -1812,7 +1815,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // to create a temporary. span_bug!(self.mir.span, "can't directly store to unaligned value"); } - llargs.push(dest.llval); + llargs.push(dest.val.llval); ReturnDest::Nothing } else { ReturnDest::Store(dest) diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index 0387c430d32..a5fd82a3054 100644 --- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs @@ -14,7 +14,7 @@ use rustc_span::{BytePos, Span}; use rustc_target::abi::{Abi, FieldIdx, FieldsShape, Size, VariantIdx}; use super::operand::{OperandRef, OperandValue}; -use super::place::PlaceRef; +use super::place::{PlaceRef, PlaceValue}; use super::{FunctionCx, LocalRef}; use std::ops::Range; @@ -252,7 +252,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // at least for the cases which LLVM handles correctly. let spill_slot = PlaceRef::alloca(bx, operand.layout); if let Some(name) = name { - bx.set_var_name(spill_slot.llval, &(name + ".dbg.spill")); + bx.set_var_name(spill_slot.val.llval, &(name + ".dbg.spill")); } operand.val.store(bx, spill_slot); spill_slot @@ -331,10 +331,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { if let Some(name) = &name { match local_ref { LocalRef::Place(place) | LocalRef::UnsizedPlace(place) => { - bx.set_var_name(place.llval, name); + bx.set_var_name(place.val.llval, name); } LocalRef::Operand(operand) => match operand.val { - OperandValue::Ref(x, ..) | OperandValue::Immediate(x) => { + OperandValue::Ref(PlaceValue { llval: x, .. }) | OperandValue::Immediate(x) => { bx.set_var_name(x, name); } OperandValue::Pair(a, b) => { @@ -417,16 +417,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let ptr_ty = Ty::new_mut_ptr(bx.tcx(), place.layout.ty); let ptr_layout = bx.layout_of(ptr_ty); let alloca = PlaceRef::alloca(bx, ptr_layout); - bx.set_var_name(alloca.llval, &(var.name.to_string() + ".dbg.spill")); + bx.set_var_name(alloca.val.llval, &(var.name.to_string() + ".dbg.spill")); // Write the pointer to the variable - bx.store(place.llval, alloca.llval, alloca.align); + bx.store_to_place(place.val.llval, alloca.val); // Point the debug info to `*alloca` for the current variable bx.dbg_var_addr( dbg_var, dbg_loc, - alloca.llval, + alloca.val.llval, Size::ZERO, &[Size::ZERO], var.fragment, @@ -435,7 +435,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.dbg_var_addr( dbg_var, dbg_loc, - base.llval, + base.val.llval, direct_offset, &indirect_offsets, var.fragment, @@ -553,7 +553,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let base = Self::spill_operand_to_stack(operand, Some(var.name.to_string()), bx); - bx.dbg_var_addr(dbg_var, dbg_loc, base.llval, Size::ZERO, &[], fragment); + bx.dbg_var_addr( + dbg_var, + dbg_loc, + base.val.llval, + Size::ZERO, + &[], + fragment, + ); } } } diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index 3e6cf0ece29..eb14a90412d 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -387,9 +387,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let success = bx.from_immediate(success); let dest = result.project_field(bx, 0); - bx.store(val, dest.llval, dest.align); + bx.store_to_place(val, dest.val); let dest = result.project_field(bx, 1); - bx.store(success, dest.llval, dest.align); + bx.store_to_place(success, dest.val); } else { invalid_monomorphization(ty); } @@ -511,7 +511,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { if !fn_abi.ret.is_ignore() { if let PassMode::Cast { .. } = &fn_abi.ret.mode { - bx.store(llval, result.llval, result.align); + bx.store_to_place(llval, result.val); } else { OperandRef::from_immediate_or_packed_pair(bx, llval, result.layout) .val diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 1f4473d2ec4..b98e90b5cde 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -2,6 +2,7 @@ use crate::base; use crate::traits::*; use rustc_index::bit_set::BitSet; use rustc_index::IndexVec; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir; use rustc_middle::mir::traversal; use rustc_middle::mir::UnwindTerminateReason; @@ -289,6 +290,12 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let mut num_untupled = None; + let codegen_fn_attrs = bx.tcx().codegen_fn_attrs(fx.instance.def_id()); + let naked = codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED); + if naked { + return vec![]; + } + let args = mir .args_iter() .enumerate() @@ -336,7 +343,7 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( if fx.fn_abi.c_variadic && arg_index == fx.fn_abi.args.len() { let va_list = PlaceRef::alloca(bx, bx.layout_of(arg_ty)); - bx.va_start(va_list.llval); + bx.va_start(va_list.val.llval); return LocalRef::Place(va_list); } diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs index ac38b91c5e0..9cf64e2d676 100644 --- a/compiler/rustc_codegen_ssa/src/mir/operand.rs +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs @@ -1,4 +1,4 @@ -use super::place::PlaceRef; +use super::place::{PlaceRef, PlaceValue}; use super::{FunctionCx, LocalRef}; use crate::size_of_val; @@ -23,11 +23,14 @@ pub enum OperandValue<V> { /// The second value, if any, is the extra data (vtable or length) /// which indicates that it refers to an unsized rvalue. /// - /// An `OperandValue` has this variant for types which are neither - /// `Immediate` nor `Pair`s. The backend value in this variant must be a - /// pointer to the *non*-immediate backend type. That pointee type is the + /// An `OperandValue` *must* be this variant for any type for which + /// [`LayoutTypeMethods::is_backend_ref`] returns `true`. + /// (That basically amounts to "isn't one of the other variants".) + /// + /// This holds a [`PlaceValue`] (like a [`PlaceRef`] does) with a pointer + /// to the location holding the value. The type behind that pointer is the /// one returned by [`LayoutTypeMethods::backend_type`]. - Ref(V, Option<V>, Align), + Ref(PlaceValue<V>), /// A single LLVM immediate value. /// /// An `OperandValue` *must* be this variant for any type for which @@ -221,7 +224,8 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { OperandValue::ZeroSized => bug!("Deref of ZST operand {:?}", self), }; let layout = cx.layout_of(projected_ty); - PlaceRef { llval: llptr, llextra, layout, align: layout.align.abi } + let val = PlaceValue { llval: llptr, llextra, align: layout.align.abi }; + PlaceRef { val, layout } } /// If this operand is a `Pair`, we return an aggregate with the two values. @@ -361,7 +365,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandValue<V> { OperandValue::Pair(bx.const_poison(ibty0), bx.const_poison(ibty1)) } else { let ptr = bx.cx().type_ptr(); - OperandValue::Ref(bx.const_poison(ptr), None, layout.align.abi) + OperandValue::Ref(PlaceValue::new_sized(bx.const_poison(ptr), layout.align.abi)) } } @@ -409,18 +413,17 @@ impl<'a, 'tcx, V: CodegenObject> OperandValue<V> { // Avoid generating stores of zero-sized values, because the only way to have a zero-sized // value is through `undef`/`poison`, and the store itself is useless. } - OperandValue::Ref(llval, llextra @ None, source_align) => { + OperandValue::Ref(val) => { assert!(dest.layout.is_sized(), "cannot directly store unsized values"); - let source_place = - PlaceRef { llval, llextra, align: source_align, layout: dest.layout }; + if val.llextra.is_some() { + bug!("cannot directly store unsized values"); + } + let source_place = PlaceRef { val, layout: dest.layout }; bx.typed_place_copy_with_flags(dest, source_place, flags); } - OperandValue::Ref(_, Some(_), _) => { - bug!("cannot directly store unsized values"); - } OperandValue::Immediate(s) => { let val = bx.from_immediate(s); - bx.store_with_flags(val, dest.llval, dest.align, flags); + bx.store_with_flags(val, dest.val.llval, dest.val.align, flags); } OperandValue::Pair(a, b) => { let Abi::ScalarPair(a_scalar, b_scalar) = dest.layout.abi else { @@ -429,12 +432,12 @@ impl<'a, 'tcx, V: CodegenObject> OperandValue<V> { let b_offset = a_scalar.size(bx).align_to(b_scalar.align(bx).abi); let val = bx.from_immediate(a); - let align = dest.align; - bx.store_with_flags(val, dest.llval, align, flags); + let align = dest.val.align; + bx.store_with_flags(val, dest.val.llval, align, flags); - let llptr = bx.inbounds_ptradd(dest.llval, bx.const_usize(b_offset.bytes())); + let llptr = bx.inbounds_ptradd(dest.val.llval, bx.const_usize(b_offset.bytes())); let val = bx.from_immediate(b); - let align = dest.align.restrict_for_offset(b_offset); + let align = dest.val.align.restrict_for_offset(b_offset); bx.store_with_flags(val, llptr, align, flags); } } @@ -454,7 +457,8 @@ impl<'a, 'tcx, V: CodegenObject> OperandValue<V> { .unwrap_or_else(|| bug!("indirect_dest has non-pointer type: {:?}", indirect_dest)) .ty; - let OperandValue::Ref(llptr, Some(llextra), _) = self else { + let OperandValue::Ref(PlaceValue { llval: llptr, llextra: Some(llextra), .. }) = self + else { bug!("store_unsized called with a sized value (or with an extern type)") }; diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index 1ec6c351e25..90627da579e 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -12,25 +12,48 @@ use rustc_middle::ty::{self, Ty}; use rustc_target::abi::{Align, FieldsShape, Int, Pointer, TagEncoding}; use rustc_target::abi::{VariantIdx, Variants}; +/// The location and extra runtime properties of the place. +/// +/// Typically found in a [`PlaceRef`] or an [`OperandValue::Ref`]. #[derive(Copy, Clone, Debug)] -pub struct PlaceRef<'tcx, V> { +pub struct PlaceValue<V> { /// A pointer to the contents of the place. pub llval: V, /// This place's extra data if it is unsized, or `None` if null. pub llextra: Option<V>, - /// The monomorphized type of this place, including variant information. - pub layout: TyAndLayout<'tcx>, - /// The alignment we know for this place. pub align: Align, } +impl<V: CodegenObject> PlaceValue<V> { + /// Constructor for the ordinary case of `Sized` types. + /// + /// Sets `llextra` to `None`. + pub fn new_sized(llval: V, align: Align) -> PlaceValue<V> { + PlaceValue { llval, llextra: None, align } + } +} + +#[derive(Copy, Clone, Debug)] +pub struct PlaceRef<'tcx, V> { + /// The location and extra runtime properties of the place. + pub val: PlaceValue<V>, + + /// The monomorphized type of this place, including variant information. + /// + /// You probably shouldn't use the alignment from this layout; + /// rather you should use the `.val.align` of the actual place, + /// which might be different from the type's normal alignment. + pub layout: TyAndLayout<'tcx>, +} + impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { pub fn new_sized(llval: V, layout: TyAndLayout<'tcx>) -> PlaceRef<'tcx, V> { assert!(layout.is_sized()); - PlaceRef { llval, llextra: None, layout, align: layout.align.abi } + let val = PlaceValue::new_sized(llval, layout.align.abi); + PlaceRef { val, layout } } pub fn new_sized_aligned( @@ -39,7 +62,8 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { align: Align, ) -> PlaceRef<'tcx, V> { assert!(layout.is_sized()); - PlaceRef { llval, llextra: None, layout, align } + let val = PlaceValue::new_sized(llval, align); + PlaceRef { val, layout } } // FIXME(eddyb) pass something else for the name so no work is done @@ -78,7 +102,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { if let FieldsShape::Array { count, .. } = self.layout.fields { if self.layout.is_unsized() { assert_eq!(count, 0); - self.llextra.unwrap() + self.val.llextra.unwrap() } else { cx.const_usize(count) } @@ -97,21 +121,27 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { ) -> Self { let field = self.layout.field(bx.cx(), ix); let offset = self.layout.fields.offset(ix); - let effective_field_align = self.align.restrict_for_offset(offset); + let effective_field_align = self.val.align.restrict_for_offset(offset); // `simple` is called when we don't need to adjust the offset to // the dynamic alignment of the field. let mut simple = || { let llval = if offset.bytes() == 0 { - self.llval + self.val.llval } else { - bx.inbounds_ptradd(self.llval, bx.const_usize(offset.bytes())) + bx.inbounds_ptradd(self.val.llval, bx.const_usize(offset.bytes())) }; PlaceRef { - llval, - llextra: if bx.cx().type_has_metadata(field.ty) { self.llextra } else { None }, + val: PlaceValue { + llval, + llextra: if bx.cx().type_has_metadata(field.ty) { + self.val.llextra + } else { + None + }, + align: effective_field_align, + }, layout: field, - align: effective_field_align, } }; @@ -142,7 +172,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { // The type `Foo<Foo<Trait>>` is represented in LLVM as `{ u16, { u16, u8 }}`, meaning that // the `y` field has 16-bit alignment. - let meta = self.llextra; + let meta = self.val.llextra; let unaligned_offset = bx.cx().const_usize(offset.bytes()); @@ -164,9 +194,10 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { debug!("struct_field_ptr: DST field offset: {:?}", offset); // Adjust pointer. - let ptr = bx.inbounds_ptradd(self.llval, offset); - - PlaceRef { llval: ptr, llextra: self.llextra, layout: field, align: effective_field_align } + let ptr = bx.inbounds_ptradd(self.val.llval, offset); + let val = + PlaceValue { llval: ptr, llextra: self.val.llextra, align: effective_field_align }; + PlaceRef { val, layout: field } } /// Obtain the actual discriminant of a value. @@ -312,10 +343,9 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { let ptr = self.project_field(bx, tag_field); let to = self.layout.ty.discriminant_for_variant(bx.tcx(), variant_index).unwrap().val; - bx.store( + bx.store_to_place( bx.cx().const_uint_big(bx.cx().backend_type(ptr.layout), to), - ptr.llval, - ptr.align, + ptr.val, ); } Variants::Multiple { @@ -357,14 +387,16 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { }; PlaceRef { - llval: bx.inbounds_gep( - bx.cx().backend_type(self.layout), - self.llval, - &[bx.cx().const_usize(0), llindex], - ), - llextra: None, + val: PlaceValue { + llval: bx.inbounds_gep( + bx.cx().backend_type(self.layout), + self.val.llval, + &[bx.cx().const_usize(0), llindex], + ), + llextra: None, + align: self.val.align.restrict_for_offset(offset), + }, layout, - align: self.align.restrict_for_offset(offset), } } @@ -389,11 +421,11 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { } pub fn storage_live<Bx: BuilderMethods<'a, 'tcx, Value = V>>(&self, bx: &mut Bx) { - bx.lifetime_start(self.llval, self.layout.size); + bx.lifetime_start(self.val.llval, self.layout.size); } pub fn storage_dead<Bx: BuilderMethods<'a, 'tcx, Value = V>>(&self, bx: &mut Bx) { - bx.lifetime_end(self.llval, self.layout.size); + bx.lifetime_end(self.val.llval, self.layout.size); } } @@ -461,8 +493,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { if subslice.layout.is_unsized() { assert!(from_end, "slice subslices should be `from_end`"); - subslice.llextra = - Some(bx.sub(cg_base.llextra.unwrap(), bx.cx().const_usize(from + to))); + subslice.val.llextra = Some( + bx.sub(cg_base.val.llextra.unwrap(), bx.cx().const_usize(from + to)), + ); } subslice diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index d62f560f11f..6725a6d9e38 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -68,13 +68,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { base::coerce_unsized_into(bx, scratch, dest); scratch.storage_dead(bx); } - OperandValue::Ref(llref, None, align) => { - let source = PlaceRef::new_sized_aligned(llref, operand.layout, align); + OperandValue::Ref(val) => { + if val.llextra.is_some() { + bug!("unsized coercion on an unsized rvalue"); + } + let source = PlaceRef { val, layout: operand.layout }; base::coerce_unsized_into(bx, source, dest); } - OperandValue::Ref(_, Some(_), _) => { - bug!("unsized coercion on an unsized rvalue"); - } OperandValue::ZeroSized => { bug!("unsized coercion on a ZST rvalue"); } @@ -95,20 +95,20 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } if let OperandValue::Immediate(v) = cg_elem.val { - let start = dest.llval; + let start = dest.val.llval; let size = bx.const_usize(dest.layout.size.bytes()); // Use llvm.memset.p0i8.* to initialize all zero arrays if bx.cx().const_to_opt_u128(v, false) == Some(0) { let fill = bx.cx().const_u8(0); - bx.memset(start, fill, size, dest.align, MemFlags::empty()); + bx.memset(start, fill, size, dest.val.align, MemFlags::empty()); return; } // Use llvm.memset.p0i8.* to initialize byte arrays let v = bx.from_immediate(v); if bx.cx().val_ty(v) == bx.cx().type_i8() { - bx.memset(start, v, size, dest.align, MemFlags::empty()); + bx.memset(start, v, size, dest.val.align, MemFlags::empty()); return; } } @@ -182,7 +182,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { OperandValue::Immediate(..) | OperandValue::Pair(..) => { // When we have immediate(s), the alignment of the source is irrelevant, // so we can store them using the destination's alignment. - src.val.store(bx, PlaceRef::new_sized_aligned(dst.llval, src.layout, dst.align)); + src.val.store( + bx, + PlaceRef::new_sized_aligned(dst.val.llval, src.layout, dst.val.align), + ); } } } @@ -217,10 +220,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let cast_kind = self.value_kind(cast); match operand.val { - OperandValue::Ref(ptr, meta, align) => { - debug_assert_eq!(meta, None); + OperandValue::Ref(source_place_val) => { + debug_assert_eq!(source_place_val.llextra, None); debug_assert!(matches!(operand_kind, OperandValueKind::Ref)); - let fake_place = PlaceRef::new_sized_aligned(ptr, cast, align); + let fake_place = PlaceRef { val: source_place_val, layout: cast }; Some(bx.load_operand(fake_place).val) } OperandValue::ZeroSized => { @@ -375,7 +378,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { ) { debug!( "codegen_rvalue_unsized(indirect_dest.llval={:?}, rvalue={:?})", - indirect_dest.llval, rvalue + indirect_dest.val.llval, rvalue ); match *rvalue { @@ -487,7 +490,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } mir::CastKind::DynStar => { let (lldata, llextra) = match operand.val { - OperandValue::Ref(_, _, _) => todo!(), + OperandValue::Ref(..) => todo!(), OperandValue::Immediate(v) => (v, None), OperandValue::Pair(v, l) => (v, Some(l)), OperandValue::ZeroSized => bug!("ZST -- which is not PointerLike -- in DynStar"), @@ -765,9 +768,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // Note: places are indirect, so storing the `llval` into the // destination effectively creates a reference. let val = if !bx.cx().type_has_metadata(ty) { - OperandValue::Immediate(cg_place.llval) + OperandValue::Immediate(cg_place.val.llval) } else { - OperandValue::Pair(cg_place.llval, cg_place.llextra.unwrap()) + OperandValue::Pair(cg_place.val.llval, cg_place.val.llextra.unwrap()) }; OperandRef { val, layout: self.cx.layout_of(mk_ptr_ty(self.cx.tcx(), ty)) } } diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index c0281e75d9d..9191618c064 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -12,7 +12,7 @@ use crate::common::{ AtomicOrdering, AtomicRmwBinOp, IntPredicate, RealPredicate, SynchronizationScope, TypeKind, }; use crate::mir::operand::{OperandRef, OperandValue}; -use crate::mir::place::PlaceRef; +use crate::mir::place::{PlaceRef, PlaceValue}; use crate::MemFlags; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; @@ -156,6 +156,10 @@ pub trait BuilderMethods<'a, 'tcx>: order: AtomicOrdering, size: Size, ) -> Self::Value; + fn load_from_place(&mut self, ty: Self::Type, place: PlaceValue<Self::Value>) -> Self::Value { + debug_assert_eq!(place.llextra, None); + self.load(ty, place.llval, place.align) + } fn load_operand(&mut self, place: PlaceRef<'tcx, Self::Value>) -> OperandRef<'tcx, Self::Value>; @@ -171,6 +175,10 @@ pub trait BuilderMethods<'a, 'tcx>: fn nonnull_metadata(&mut self, load: Self::Value); fn store(&mut self, val: Self::Value, ptr: Self::Value, align: Align) -> Self::Value; + fn store_to_place(&mut self, val: Self::Value, place: PlaceValue<Self::Value>) -> Self::Value { + debug_assert_eq!(place.llextra, None); + self.store(val, place.llval, place.align) + } fn store_with_flags( &mut self, val: Self::Value, @@ -290,14 +298,14 @@ pub trait BuilderMethods<'a, 'tcx>: src: PlaceRef<'tcx, Self::Value>, flags: MemFlags, ) { - debug_assert!(src.llextra.is_none(), "cannot directly copy from unsized values"); - debug_assert!(dst.llextra.is_none(), "cannot directly copy into unsized values"); + debug_assert!(src.val.llextra.is_none(), "cannot directly copy from unsized values"); + debug_assert!(dst.val.llextra.is_none(), "cannot directly copy into unsized values"); debug_assert_eq!(dst.layout.size, src.layout.size); if flags.contains(MemFlags::NONTEMPORAL) { // HACK(nox): This is inefficient but there is no nontemporal memcpy. let ty = self.backend_type(dst.layout); - let val = self.load(ty, src.llval, src.align); - self.store_with_flags(val, dst.llval, dst.align, flags); + let val = self.load_from_place(ty, src.val); + self.store_with_flags(val, dst.val.llval, dst.val.align, flags); } else if self.sess().opts.optimize == OptLevel::No && self.is_backend_immediate(dst.layout) { // If we're not optimizing, the aliasing information from `memcpy` @@ -306,7 +314,7 @@ pub trait BuilderMethods<'a, 'tcx>: temp.val.store_with_flags(self, dst, flags); } else if !dst.layout.is_zst() { let bytes = self.const_usize(dst.layout.size.bytes()); - self.memcpy(dst.llval, dst.align, src.llval, src.align, bytes, flags); + self.memcpy(dst.val.llval, dst.val.align, src.val.llval, src.val.align, bytes, flags); } } diff --git a/compiler/rustc_data_structures/src/sip128.rs b/compiler/rustc_data_structures/src/sip128.rs index 4a0ed87f77c..4c9acfe0f71 100644 --- a/compiler/rustc_data_structures/src/sip128.rs +++ b/compiler/rustc_data_structures/src/sip128.rs @@ -1,5 +1,8 @@ //! This is a copy of `core::hash::sip` adapted to providing 128 bit hashes. +// This code is very hot and uses lots of arithmetic, avoid overflow checks for performance. +// See https://github.com/rust-lang/rust/pull/119440#issuecomment-1874255727 +use rustc_serialize::int_overflow::{DebugStrictAdd, DebugStrictSub}; use std::hash::Hasher; use std::mem::{self, MaybeUninit}; use std::ptr; @@ -103,19 +106,19 @@ unsafe fn copy_nonoverlapping_small(src: *const u8, dst: *mut u8, count: usize) } let mut i = 0; - if i + 3 < count { + if i.debug_strict_add(3) < count { ptr::copy_nonoverlapping(src.add(i), dst.add(i), 4); - i += 4; + i = i.debug_strict_add(4); } - if i + 1 < count { + if i.debug_strict_add(1) < count { ptr::copy_nonoverlapping(src.add(i), dst.add(i), 2); - i += 2 + i = i.debug_strict_add(2) } if i < count { *dst.add(i) = *src.add(i); - i += 1; + i = i.debug_strict_add(1); } debug_assert_eq!(i, count); @@ -211,14 +214,14 @@ impl SipHasher128 { debug_assert!(nbuf < BUFFER_SIZE); debug_assert!(nbuf + LEN < BUFFER_WITH_SPILL_SIZE); - if nbuf + LEN < BUFFER_SIZE { + if nbuf.debug_strict_add(LEN) < BUFFER_SIZE { unsafe { // The memcpy call is optimized away because the size is known. let dst = (self.buf.as_mut_ptr() as *mut u8).add(nbuf); ptr::copy_nonoverlapping(bytes.as_ptr(), dst, LEN); } - self.nbuf = nbuf + LEN; + self.nbuf = nbuf.debug_strict_add(LEN); return; } @@ -265,8 +268,9 @@ impl SipHasher128 { // This function should only be called when the write fills the buffer. // Therefore, when LEN == 1, the new `self.nbuf` must be zero. // LEN is statically known, so the branch is optimized away. - self.nbuf = if LEN == 1 { 0 } else { nbuf + LEN - BUFFER_SIZE }; - self.processed += BUFFER_SIZE; + self.nbuf = + if LEN == 1 { 0 } else { nbuf.debug_strict_add(LEN).debug_strict_sub(BUFFER_SIZE) }; + self.processed = self.processed.debug_strict_add(BUFFER_SIZE); } } @@ -277,7 +281,7 @@ impl SipHasher128 { let nbuf = self.nbuf; debug_assert!(nbuf < BUFFER_SIZE); - if nbuf + length < BUFFER_SIZE { + if nbuf.debug_strict_add(length) < BUFFER_SIZE { unsafe { let dst = (self.buf.as_mut_ptr() as *mut u8).add(nbuf); @@ -289,7 +293,7 @@ impl SipHasher128 { } } - self.nbuf = nbuf + length; + self.nbuf = nbuf.debug_strict_add(length); return; } @@ -315,7 +319,7 @@ impl SipHasher128 { // This function should only be called when the write fills the buffer, // so we know that there is enough input to fill the current element. let valid_in_elem = nbuf % ELEM_SIZE; - let needed_in_elem = ELEM_SIZE - valid_in_elem; + let needed_in_elem = ELEM_SIZE.debug_strict_sub(valid_in_elem); let src = msg.as_ptr(); let dst = (self.buf.as_mut_ptr() as *mut u8).add(nbuf); @@ -327,7 +331,7 @@ impl SipHasher128 { // ELEM_SIZE` to show the compiler that this loop's upper bound is > 0. // We know that is true, because last step ensured we have a full // element in the buffer. - let last = nbuf / ELEM_SIZE + 1; + let last = (nbuf / ELEM_SIZE).debug_strict_add(1); for i in 0..last { let elem = self.buf.get_unchecked(i).assume_init().to_le(); @@ -338,7 +342,7 @@ impl SipHasher128 { // Process the remaining element-sized chunks of input. let mut processed = needed_in_elem; - let input_left = length - processed; + let input_left = length.debug_strict_sub(processed); let elems_left = input_left / ELEM_SIZE; let extra_bytes_left = input_left % ELEM_SIZE; @@ -347,7 +351,7 @@ impl SipHasher128 { self.state.v3 ^= elem; Sip13Rounds::c_rounds(&mut self.state); self.state.v0 ^= elem; - processed += ELEM_SIZE; + processed = processed.debug_strict_add(ELEM_SIZE); } // Copy remaining input into start of buffer. @@ -356,7 +360,7 @@ impl SipHasher128 { copy_nonoverlapping_small(src, dst, extra_bytes_left); self.nbuf = extra_bytes_left; - self.processed += nbuf + processed; + self.processed = self.processed.debug_strict_add(nbuf.debug_strict_add(processed)); } } @@ -394,7 +398,7 @@ impl SipHasher128 { }; // Finalize the hash. - let length = self.processed + self.nbuf; + let length = self.processed.debug_strict_add(self.nbuf); let b: u64 = ((length as u64 & 0xff) << 56) | elem; state.v3 ^= b; diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index e4fb13822f8..a8bba3afb7e 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -5,7 +5,6 @@ edition = "2021" [dependencies] # tidy-alphabetical-start -ctrlc = "3.4.4" rustc_ast = { path = "../rustc_ast" } rustc_ast_lowering = { path = "../rustc_ast_lowering" } rustc_ast_passes = { path = "../rustc_ast_passes" } @@ -66,6 +65,11 @@ features = [ "Win32_System_Diagnostics_Debug", ] +[target.'cfg(not(target_family = "wasm"))'.dependencies] +# tidy-alphabetical-start +ctrlc = "3.4.4" +# tidy-alphabetical-end + [features] # tidy-alphabetical-start llvm = ['rustc_interface/llvm'] diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 1f44621736c..b3cba4dbfc2 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -1500,6 +1500,7 @@ pub fn init_logger(early_dcx: &EarlyDiagCtxt, cfg: rustc_log::LoggerConfig) { /// Install our usual `ctrlc` handler, which sets [`rustc_const_eval::CTRL_C_RECEIVED`]. /// Making this handler optional lets tools can install a different handler, if they wish. pub fn install_ctrlc_handler() { + #[cfg(not(target_family = "wasm"))] ctrlc::set_handler(move || { // Indicate that we have been signaled to stop. If we were already signaled, exit // immediately. In our interpreter loop we try to consult this value often, but if for diff --git a/compiler/rustc_error_codes/src/error_codes/E0384.md b/compiler/rustc_error_codes/src/error_codes/E0384.md index e21fac0797c..6680dbed3dd 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0384.md +++ b/compiler/rustc_error_codes/src/error_codes/E0384.md @@ -18,3 +18,16 @@ fn main() { x = 5; } ``` + +Alternatively, you might consider initializing a new variable: either with a new +bound name or (by [shadowing]) with the bound name of your existing variable. +For example: + +[shadowing]: https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#shadowing + +``` +fn main() { + let x = 3; + let x = 5; +} +``` diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index bd8e78bda26..6ce3fa3535d 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -1513,7 +1513,9 @@ impl HumanEmitter { for line_idx in 0..annotated_file.lines.len() { let file = annotated_file.file.clone(); let line = &annotated_file.lines[line_idx]; - if let Some(source_string) = file.get_line(line.line_index - 1) { + if let Some(source_string) = + line.line_index.checked_sub(1).and_then(|l| file.get_line(l)) + { let leading_whitespace = source_string .chars() .take_while(|c| c.is_whitespace()) @@ -1553,7 +1555,10 @@ impl HumanEmitter { for line in &annotated_file.lines { max_line_len = max( max_line_len, - annotated_file.file.get_line(line.line_index - 1).map_or(0, |s| s.len()), + line.line_index + .checked_sub(1) + .and_then(|l| annotated_file.file.get_line(l)) + .map_or(0, |s| s.len()), ); for ann in &line.annotations { span_right_margin = max(span_right_margin, ann.start_col.display); diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index db94e0b08bc..4f62323231a 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -1082,7 +1082,7 @@ pub fn is_builtin_attr_name(name: Symbol) -> bool { /// This means it can be used cross crate. pub fn encode_cross_crate(name: Symbol) -> bool { if let Some(attr) = BUILTIN_ATTRIBUTE_MAP.get(&name) { - if attr.encode_cross_crate == EncodeCrossCrate::Yes { true } else { false } + attr.encode_cross_crate == EncodeCrossCrate::Yes } else { true } diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 8d4ae10d4bf..216b89fd4f1 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -22,7 +22,6 @@ use rustc_middle::ty::{ AdtDef, ParamEnv, RegionKind, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, }; use rustc_session::lint::builtin::{UNINHABITED_STATIC, UNSUPPORTED_CALLING_CONVENTIONS}; -use rustc_span::symbol::sym; use rustc_target::abi::FieldIdx; use rustc_trait_selection::traits::error_reporting::on_unimplemented::OnUnimplementedDirective; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; @@ -506,7 +505,6 @@ fn check_static_linkage(tcx: TyCtxt<'_>, def_id: LocalDefId) { } pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) { - let _indenter = indenter(); match tcx.def_kind(def_id) { DefKind::Static { .. } => { tcx.ensure().typeck(def_id); diff --git a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs index 1958a80d47c..45ccd0fa2e0 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs @@ -143,7 +143,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { }; assert!( ty.is_manually_drop(), - "expected first field of `MaybeUnit` to be `ManuallyDrop`" + "expected first field of `MaybeUninit` to be `ManuallyDrop`" ); let fields = &ty.non_enum_variant().fields; let ty = fields[FieldIdx::ZERO].ty(self.tcx, args); diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 8760901b71b..024a0433b8c 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -81,6 +81,7 @@ use rustc_errors::ErrorGuaranteed; use rustc_errors::{pluralize, struct_span_code_err, Diag}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::Visitor; +use rustc_hir::Mutability; use rustc_index::bit_set::BitSet; use rustc_infer::infer::error_reporting::ObligationCauseExt as _; use rustc_infer::infer::outlives::env::OutlivesEnvironment; @@ -91,17 +92,17 @@ use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{GenericArgs, GenericArgsRef}; use rustc_session::parse::feature_err; -use rustc_span::symbol::{kw, Ident}; -use rustc_span::{self, def_id::CRATE_DEF_ID, BytePos, Span, Symbol, DUMMY_SP}; +use rustc_span::symbol::{kw, sym, Ident}; +use rustc_span::{def_id::CRATE_DEF_ID, BytePos, Span, Symbol, DUMMY_SP}; use rustc_target::abi::VariantIdx; use rustc_target::spec::abi::Abi; +use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::error_reporting::suggestions::ReturnsVisitor; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; use rustc_trait_selection::traits::ObligationCtxt; use crate::errors; use crate::require_c_abi_if_c_variadic; -use crate::util::common::indenter; use self::compare_impl_item::collect_return_position_impl_trait_in_trait_tys; use self::region::region_scope_tree; @@ -466,14 +467,64 @@ fn fn_sig_suggestion<'tcx>( ) } -pub fn ty_kind_suggestion(ty: Ty<'_>) -> Option<&'static str> { +pub fn ty_kind_suggestion<'tcx>(ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Option<String> { + // Keep in sync with `rustc_borrowck/src/diagnostics/conflict_errors.rs:ty_kind_suggestion`. + // FIXME: deduplicate the above. + let implements_default = |ty| { + let Some(default_trait) = tcx.get_diagnostic_item(sym::Default) else { + return false; + }; + let infcx = tcx.infer_ctxt().build(); + infcx + .type_implements_trait(default_trait, [ty], ty::ParamEnv::reveal_all()) + .must_apply_modulo_regions() + }; Some(match ty.kind() { - ty::Bool => "true", - ty::Char => "'a'", - ty::Int(_) | ty::Uint(_) => "42", - ty::Float(_) => "3.14159", - ty::Error(_) | ty::Never => return None, - _ => "value", + ty::Never | ty::Error(_) => return None, + ty::Bool => "false".to_string(), + ty::Char => "\'x\'".to_string(), + ty::Int(_) | ty::Uint(_) => "42".into(), + ty::Float(_) => "3.14159".into(), + ty::Slice(_) => "[]".to_string(), + ty::Adt(def, _) if Some(def.did()) == tcx.get_diagnostic_item(sym::Vec) => { + "vec![]".to_string() + } + ty::Adt(def, _) if Some(def.did()) == tcx.get_diagnostic_item(sym::String) => { + "String::new()".to_string() + } + ty::Adt(def, args) if def.is_box() => { + format!("Box::new({})", ty_kind_suggestion(args[0].expect_ty(), tcx)?) + } + ty::Adt(def, _) if Some(def.did()) == tcx.get_diagnostic_item(sym::Option) => { + "None".to_string() + } + ty::Adt(def, args) if Some(def.did()) == tcx.get_diagnostic_item(sym::Result) => { + format!("Ok({})", ty_kind_suggestion(args[0].expect_ty(), tcx)?) + } + ty::Adt(_, _) if implements_default(ty) => "Default::default()".to_string(), + ty::Ref(_, ty, mutability) => { + if let (ty::Str, Mutability::Not) = (ty.kind(), mutability) { + "\"\"".to_string() + } else { + let Some(ty) = ty_kind_suggestion(*ty, tcx) else { + return None; + }; + format!("&{}{ty}", mutability.prefix_str()) + } + } + ty::Array(ty, len) => format!( + "[{}; {}]", + ty_kind_suggestion(*ty, tcx)?, + len.eval_target_usize(tcx, ty::ParamEnv::reveal_all()), + ), + ty::Tuple(tys) => format!( + "({})", + tys.iter() + .map(|ty| ty_kind_suggestion(ty, tcx)) + .collect::<Option<Vec<String>>>()? + .join(", ") + ), + _ => "value".to_string(), }) } @@ -511,7 +562,7 @@ fn suggestion_signature<'tcx>( } ty::AssocKind::Const => { let ty = tcx.type_of(assoc.def_id).instantiate_identity(); - let val = ty_kind_suggestion(ty).unwrap_or("todo!()"); + let val = ty_kind_suggestion(ty, tcx).unwrap_or_else(|| "value".to_string()); format!("const {}: {} = {};", assoc.name, ty, val) } } diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index a705d3bc107..c1bf65367aa 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -30,7 +30,7 @@ use rustc_middle::query::Providers; use rustc_middle::ty::util::{Discr, IntTypeExt}; use rustc_middle::ty::{self, AdtKind, Const, IsSuggestable, ToPredicate, Ty, TyCtxt}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::Span; +use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::FieldIdx; use rustc_target::spec::abi; use rustc_trait_selection::infer::InferCtxtExt; @@ -1383,7 +1383,9 @@ fn infer_return_ty_for_fn_sig<'tcx>( Applicability::MachineApplicable, ); should_recover = true; - } else if let Some(sugg) = suggest_impl_trait(tcx, ret_ty, ty.span, def_id) { + } else if let Some(sugg) = + suggest_impl_trait(&tcx.infer_ctxt().build(), tcx.param_env(def_id), ret_ty) + { diag.span_suggestion( ty.span, "replace with an appropriate return type", @@ -1426,11 +1428,10 @@ fn infer_return_ty_for_fn_sig<'tcx>( } } -fn suggest_impl_trait<'tcx>( - tcx: TyCtxt<'tcx>, +pub fn suggest_impl_trait<'tcx>( + infcx: &InferCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, ret_ty: Ty<'tcx>, - span: Span, - def_id: LocalDefId, ) -> Option<String> { let format_as_assoc: fn(_, _, _, _, _) -> _ = |tcx: TyCtxt<'tcx>, @@ -1464,24 +1465,28 @@ fn suggest_impl_trait<'tcx>( for (trait_def_id, assoc_item_def_id, formatter) in [ ( - tcx.get_diagnostic_item(sym::Iterator), - tcx.get_diagnostic_item(sym::IteratorItem), + infcx.tcx.get_diagnostic_item(sym::Iterator), + infcx.tcx.get_diagnostic_item(sym::IteratorItem), format_as_assoc, ), ( - tcx.lang_items().future_trait(), - tcx.get_diagnostic_item(sym::FutureOutput), + infcx.tcx.lang_items().future_trait(), + infcx.tcx.get_diagnostic_item(sym::FutureOutput), format_as_assoc, ), - (tcx.lang_items().fn_trait(), tcx.lang_items().fn_once_output(), format_as_parenthesized), ( - tcx.lang_items().fn_mut_trait(), - tcx.lang_items().fn_once_output(), + infcx.tcx.lang_items().fn_trait(), + infcx.tcx.lang_items().fn_once_output(), + format_as_parenthesized, + ), + ( + infcx.tcx.lang_items().fn_mut_trait(), + infcx.tcx.lang_items().fn_once_output(), format_as_parenthesized, ), ( - tcx.lang_items().fn_once_trait(), - tcx.lang_items().fn_once_output(), + infcx.tcx.lang_items().fn_once_trait(), + infcx.tcx.lang_items().fn_once_output(), format_as_parenthesized, ), ] { @@ -1491,36 +1496,45 @@ fn suggest_impl_trait<'tcx>( let Some(assoc_item_def_id) = assoc_item_def_id else { continue; }; - if tcx.def_kind(assoc_item_def_id) != DefKind::AssocTy { + if infcx.tcx.def_kind(assoc_item_def_id) != DefKind::AssocTy { continue; } - let param_env = tcx.param_env(def_id); - let infcx = tcx.infer_ctxt().build(); - let args = ty::GenericArgs::for_item(tcx, trait_def_id, |param, _| { - if param.index == 0 { ret_ty.into() } else { infcx.var_for_def(span, param) } + let sugg = infcx.probe(|_| { + let args = ty::GenericArgs::for_item(infcx.tcx, trait_def_id, |param, _| { + if param.index == 0 { ret_ty.into() } else { infcx.var_for_def(DUMMY_SP, param) } + }); + if !infcx + .type_implements_trait(trait_def_id, args, param_env) + .must_apply_modulo_regions() + { + return None; + } + let ocx = ObligationCtxt::new(&infcx); + let item_ty = ocx.normalize( + &ObligationCause::dummy(), + param_env, + Ty::new_projection(infcx.tcx, assoc_item_def_id, args), + ); + // FIXME(compiler-errors): We may benefit from resolving regions here. + if ocx.select_where_possible().is_empty() + && let item_ty = infcx.resolve_vars_if_possible(item_ty) + && let Some(item_ty) = item_ty.make_suggestable(infcx.tcx, false, None) + && let Some(sugg) = formatter( + infcx.tcx, + infcx.resolve_vars_if_possible(args), + trait_def_id, + assoc_item_def_id, + item_ty, + ) + { + return Some(sugg); + } + + None }); - if !infcx.type_implements_trait(trait_def_id, args, param_env).must_apply_modulo_regions() { - continue; - } - let ocx = ObligationCtxt::new(&infcx); - let item_ty = ocx.normalize( - &ObligationCause::misc(span, def_id), - param_env, - Ty::new_projection(tcx, assoc_item_def_id, args), - ); - // FIXME(compiler-errors): We may benefit from resolving regions here. - if ocx.select_where_possible().is_empty() - && let item_ty = infcx.resolve_vars_if_possible(item_ty) - && let Some(item_ty) = item_ty.make_suggestable(tcx, false, None) - && let Some(sugg) = formatter( - tcx, - infcx.resolve_vars_if_possible(args), - trait_def_id, - assoc_item_def_id, - item_ty, - ) - { - return Some(sugg); + + if sugg.is_some() { + return sugg; } } None diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index affd678fc6c..3d16f1420d9 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -718,6 +718,11 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { } #[instrument(level = "debug", skip(self))] + fn visit_pattern_type_pattern(&mut self, p: &'tcx hir::Pat<'tcx>) { + intravisit::walk_pat(self, p) + } + + #[instrument(level = "debug", skip(self))] fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) { use self::hir::TraitItemKind::*; match trait_item.kind { diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index 70f09dd6175..822bf95305f 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -1134,7 +1134,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { for (what, span) in types_and_spans { err.span_label(span, format!("not allowed on {what}")); } - generics_args_err_extend(self.tcx(), segments.clone(), &mut err, err_extend); + generics_args_err_extend(self.tcx(), segments, &mut err, err_extend); let reported = err.emit(); self.set_tainted_by_errors(reported); reported diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 9fb8b4ac40e..63aeb165a48 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -2234,11 +2234,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .type_of(def_id) .no_bound_vars() .expect("const parameter types cannot be generic"); - let item_def_id = tcx.parent(def_id); - let generics = tcx.generics_of(item_def_id); - let index = generics.param_def_id_to_index[&def_id]; - let name = tcx.item_name(def_id); - ty::Const::new_param(tcx, ty::ParamConst::new(index, name), ty) + self.lower_const_param(expr.hir_id, ty) } _ => { diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 5a374fa5e04..c374f9762d6 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -103,7 +103,6 @@ use rustc_middle::middle; use rustc_middle::mir::interpret::GlobalId; use rustc_middle::query::Providers; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_middle::util; use rustc_session::parse::feature_err; use rustc_span::{symbol::sym, Span}; use rustc_target::spec::abi::Abi; diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index a590c648d20..39312614c1b 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -1142,7 +1142,7 @@ impl<'a> State<'a> { } fn print_expr_binary(&mut self, op: hir::BinOp, lhs: &hir::Expr<'_>, rhs: &hir::Expr<'_>) { - let assoc_op = bin_op_to_assoc_op(op.node); + let assoc_op = AssocOp::from_ast_binop(op.node); let prec = assoc_op.precedence() as i8; let fixity = assoc_op.fixity(); @@ -2328,33 +2328,6 @@ fn stmt_ends_with_semi(stmt: &hir::StmtKind<'_>) -> bool { } } -fn bin_op_to_assoc_op(op: hir::BinOpKind) -> AssocOp { - use crate::hir::BinOpKind::*; - match op { - Add => AssocOp::Add, - Sub => AssocOp::Subtract, - Mul => AssocOp::Multiply, - Div => AssocOp::Divide, - Rem => AssocOp::Modulus, - - And => AssocOp::LAnd, - Or => AssocOp::LOr, - - BitXor => AssocOp::BitXor, - BitAnd => AssocOp::BitAnd, - BitOr => AssocOp::BitOr, - Shl => AssocOp::ShiftLeft, - Shr => AssocOp::ShiftRight, - - Eq => AssocOp::Equal, - Lt => AssocOp::Less, - Le => AssocOp::LessEqual, - Ne => AssocOp::NotEqual, - Ge => AssocOp::GreaterEqual, - Gt => AssocOp::Greater, - } -} - /// Expressions that syntactically contain an "exterior" struct literal, i.e., not surrounded by any /// parens or other delimiters, e.g., `X { y: 1 }`, `X { y: 1 }.method()`, `foo == X { y: 1 }` and /// `X { y: 1 } == foo` all do, but `(X { y: 1 }) == foo` does not. diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 51d01afc4eb..720f4927ea8 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -2004,16 +2004,17 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { } } - let parent_id = fcx.tcx.hir().get_parent_item(id); - let mut parent_item = fcx.tcx.hir_node_by_def_id(parent_id.def_id); + let mut parent_id = fcx.tcx.hir().get_parent_item(id).def_id; + let mut parent_item = fcx.tcx.hir_node_by_def_id(parent_id); // When suggesting return, we need to account for closures and async blocks, not just items. for (_, node) in fcx.tcx.hir().parent_iter(id) { match node { hir::Node::Expr(&hir::Expr { - kind: hir::ExprKind::Closure(hir::Closure { .. }), + kind: hir::ExprKind::Closure(hir::Closure { def_id, .. }), .. }) => { parent_item = node; + parent_id = *def_id; break; } hir::Node::Item(_) | hir::Node::TraitItem(_) | hir::Node::ImplItem(_) => break, @@ -2023,13 +2024,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { if let (Some(expr), Some(_), Some(fn_decl)) = (expression, blk_id, parent_item.fn_decl()) { fcx.suggest_missing_break_or_return_expr( - &mut err, - expr, - fn_decl, - expected, - found, - id, - parent_id.into(), + &mut err, expr, fn_decl, expected, found, id, parent_id, ); } diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 7a1a2c498aa..8f30c3fd377 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -694,10 +694,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); let error = Some(Sorts(ExpectedFound { expected: ty, found: e_ty })); self.annotate_loop_expected_due_to_inference(err, expr, error); - if let Some(val) = ty_kind_suggestion(ty) { + if let Some(val) = ty_kind_suggestion(ty, tcx) { err.span_suggestion_verbose( expr.span.shrink_to_hi(), - "give it a value of the expected type", + "give the `break` a value of the expected type", format!(" {val}"), Applicability::HasPlaceholders, ); diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index f1e82543a99..2580179ce5b 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -561,9 +561,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Unify `interior` with `witness` and collect all the resulting obligations. let span = self.tcx.hir().body(body_id).value.span; + let ty::Infer(ty::InferTy::TyVar(_)) = interior.kind() else { + span_bug!(span, "coroutine interior witness not infer: {:?}", interior.kind()) + }; let ok = self .at(&self.misc(span), self.param_env) - .eq(DefineOpaqueTypes::No, interior, witness) + // Will never define opaque types, as all we do is instantiate a type variable. + .eq(DefineOpaqueTypes::Yes, interior, witness) .expect("Failed to unify coroutine interior type"); let mut obligations = ok.obligations; @@ -942,7 +946,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(in super::super) fn get_node_fn_decl( &self, node: Node<'tcx>, - ) -> Option<(hir::HirId, &'tcx hir::FnDecl<'tcx>, Ident, bool)> { + ) -> Option<(LocalDefId, &'tcx hir::FnDecl<'tcx>, Ident, bool)> { match node { Node::Item(&hir::Item { ident, @@ -953,25 +957,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // This is less than ideal, it will not suggest a return type span on any // method called `main`, regardless of whether it is actually the entry point, // but it will still present it as the reason for the expected type. - Some(( - hir::HirId::make_owner(owner_id.def_id), - &sig.decl, - ident, - ident.name != sym::main, - )) + Some((owner_id.def_id, &sig.decl, ident, ident.name != sym::main)) } Node::TraitItem(&hir::TraitItem { ident, kind: hir::TraitItemKind::Fn(ref sig, ..), owner_id, .. - }) => Some((hir::HirId::make_owner(owner_id.def_id), &sig.decl, ident, true)), + }) => Some((owner_id.def_id, &sig.decl, ident, true)), Node::ImplItem(&hir::ImplItem { ident, kind: hir::ImplItemKind::Fn(ref sig, ..), owner_id, .. - }) => Some((hir::HirId::make_owner(owner_id.def_id), &sig.decl, ident, false)), + }) => Some((owner_id.def_id, &sig.decl, ident, false)), Node::Expr(&hir::Expr { hir_id, kind: @@ -1001,12 +1000,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) => (ident, sig, owner_id), _ => return None, }; - Some(( - hir::HirId::make_owner(owner_id.def_id), - &sig.decl, - ident, - ident.name != sym::main, - )) + Some((owner_id.def_id, &sig.decl, ident, ident.name != sym::main)) } _ => None, } @@ -1017,7 +1011,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn get_fn_decl( &self, blk_id: hir::HirId, - ) -> Option<(hir::HirId, &'tcx hir::FnDecl<'tcx>, bool)> { + ) -> Option<(LocalDefId, &'tcx hir::FnDecl<'tcx>, bool)> { // Get enclosing Fn, if it is a function or a trait method, unless there's a `loop` or // `while` before reaching it, as block tail returns are not available in them. self.tcx.hir().get_return_block(blk_id).and_then(|blk_id| { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 442bfd75746..ce203eae95f 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -9,6 +9,7 @@ use crate::method::probe::{IsSuggestion, Mode, ProbeScope}; use crate::rustc_middle::ty::Article; use core::cmp::min; use core::iter; +use hir::def_id::LocalDefId; use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX}; use rustc_data_structures::packed::Pu128; use rustc_errors::{Applicability, Diag, MultiSpan}; @@ -20,6 +21,7 @@ use rustc_hir::{ CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, GenericBound, HirId, Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicate, }; +use rustc_hir_analysis::collect::suggest_impl_trait; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; use rustc_infer::traits::{self}; use rustc_middle::lint::in_external_macro; @@ -795,7 +797,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, found: Ty<'tcx>, can_suggest: bool, - fn_id: hir::HirId, + fn_id: LocalDefId, ) -> bool { let found = self.resolve_numeric_literals_with_default(self.resolve_vars_if_possible(found)); @@ -814,17 +816,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { errors::AddReturnTypeSuggestion::Add { span, found: found.to_string() }, ); return true; - } else if let ty::Closure(_, args) = found.kind() - // FIXME(compiler-errors): Get better at printing binders... - && let closure = args.as_closure() - && closure.sig().is_suggestable(self.tcx, false) - { + } else if let Some(sugg) = suggest_impl_trait(self, self.param_env, found) { err.subdiagnostic( self.dcx(), - errors::AddReturnTypeSuggestion::Add { - span, - found: closure.print_as_impl_trait().to_string(), - }, + errors::AddReturnTypeSuggestion::Add { span, found: sugg }, ); return true; } else { @@ -929,7 +924,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err: &mut Diag<'_>, expected: Ty<'tcx>, found: Ty<'tcx>, - fn_id: hir::HirId, + fn_id: LocalDefId, ) { // Only apply the suggestion if: // - the return type is a generic parameter @@ -943,7 +938,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ty::Param(expected_ty_as_param) = expected.kind() else { return }; - let fn_node = self.tcx.hir_node(fn_id); + let fn_node = self.tcx.hir_node_by_def_id(fn_id); let hir::Node::Item(hir::Item { kind: @@ -1037,7 +1032,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, found: Ty<'tcx>, id: hir::HirId, - fn_id: hir::HirId, + fn_id: LocalDefId, ) { if !expected.is_unit() { return; @@ -1089,11 +1084,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let can_return = match fn_decl.output { hir::FnRetTy::Return(ty) => { let ty = self.lowerer().lower_ty(ty); - let bound_vars = self.tcx.late_bound_vars(fn_id); + let bound_vars = self.tcx.late_bound_vars(self.tcx.local_def_id_to_hir_id(fn_id)); let ty = self .tcx .instantiate_bound_regions_with_erased(Binder::bind_with_vars(ty, bound_vars)); - let ty = match self.tcx.asyncness(fn_id.owner) { + let ty = match self.tcx.asyncness(fn_id) { ty::Asyncness::Yes => self.get_impl_future_output_ty(ty).unwrap_or_else(|| { span_bug!( fn_decl.output.span(), @@ -1114,8 +1109,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => false, }; if can_return - && let Some(owner_node) = self.tcx.hir_node(fn_id).as_owner() - && let Some(span) = expr.span.find_ancestor_inside(*owner_node.span()) + && let Some(span) = expr.span.find_ancestor_inside( + self.tcx.hir().span_with_body(self.tcx.local_def_id_to_hir_id(fn_id)), + ) { err.multipart_suggestion( "you might have meant to return this value", @@ -1291,12 +1287,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::TraitRef::new(self.tcx, into_def_id, [expr_ty, expected_ty]), )) { - let mut span = expr.span; - while expr.span.eq_ctxt(span) - && let Some(parent_callsite) = span.parent_callsite() - { - span = parent_callsite; - } + let span = expr.span.find_oldest_ancestor_in_same_ctxt(); let mut sugg = if expr.precedence().order() >= PREC_POSTFIX { vec![(span.shrink_to_hi(), ".into()".to_owned())] @@ -1901,12 +1892,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None => sugg.to_string(), }; - err.span_suggestion_verbose( - expr.span.shrink_to_hi(), - msg, - sugg, - Applicability::HasPlaceholders, - ); + let span = expr.span.find_oldest_ancestor_in_same_ctxt(); + err.span_suggestion_verbose(span.shrink_to_hi(), msg, sugg, Applicability::HasPlaceholders); return true; } diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 700dde184f2..476df9ae793 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -56,7 +56,7 @@ use rustc_data_structures::unord::UnordSet; use rustc_errors::{codes::*, struct_span_code_err, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::intravisit::Visitor; +use rustc_hir::intravisit::{Map, Visitor}; use rustc_hir::{HirIdMap, Node}; use rustc_hir_analysis::check::check_abi; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; @@ -436,6 +436,28 @@ fn fatally_break_rust(tcx: TyCtxt<'_>, span: Span) -> ! { diag.emit() } +pub fn lookup_method_for_diagnostic<'tcx>( + tcx: TyCtxt<'tcx>, + (def_id, hir_id): (LocalDefId, hir::HirId), +) -> Option<DefId> { + let root_ctxt = TypeckRootCtxt::new(tcx, def_id); + let param_env = tcx.param_env(def_id); + let fn_ctxt = FnCtxt::new(&root_ctxt, param_env, def_id); + let hir::Node::Expr(expr) = tcx.hir().hir_node(hir_id) else { + return None; + }; + let hir::ExprKind::MethodCall(segment, rcvr, _, _) = expr.kind else { + return None; + }; + let tables = tcx.typeck(def_id); + // The found `Self` type of the method call. + let possible_rcvr_ty = tables.node_type_opt(rcvr.hir_id)?; + fn_ctxt + .lookup_method_for_diagnostic(possible_rcvr_ty, segment, expr.span, expr, rcvr) + .ok() + .map(|method| method.def_id) +} + pub fn provide(providers: &mut Providers) { method::provide(providers); *providers = Providers { @@ -443,6 +465,7 @@ pub fn provide(providers: &mut Providers) { diagnostic_only_typeck, has_typeck_results, used_trait_imports, + lookup_method_for_diagnostic: lookup_method_for_diagnostic, ..*providers }; } diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index a199f57aad9..754866c85c4 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -529,16 +529,44 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MachineApplicable, ); } - if let ty::RawPtr(_, _) = &rcvr_ty.kind() { - err.note( - "try using `<*const T>::as_ref()` to get a reference to the \ - type behind the pointer: https://doc.rust-lang.org/std/\ - primitive.pointer.html#method.as_ref", - ); - err.note( - "using `<*const T>::as_ref()` on a pointer which is unaligned or points \ - to invalid or uninitialized memory is undefined behavior", + + // on pointers, check if the method would exist on a reference + if let SelfSource::MethodCall(rcvr_expr) = source + && let ty::RawPtr(ty, ptr_mutbl) = *rcvr_ty.kind() + && let Ok(pick) = self.lookup_probe_for_diagnostic( + item_name, + Ty::new_ref(tcx, ty::Region::new_error_misc(tcx), ty, ptr_mutbl), + self.tcx.hir().expect_expr(self.tcx.parent_hir_id(rcvr_expr.hir_id)), + ProbeScope::TraitsInScope, + None, + ) + && let ty::Ref(_, _, sugg_mutbl) = *pick.self_ty.kind() + && (sugg_mutbl.is_not() || ptr_mutbl.is_mut()) + { + let (method, method_anchor) = match sugg_mutbl { + Mutability::Not => { + let method_anchor = match ptr_mutbl { + Mutability::Not => "as_ref", + Mutability::Mut => "as_ref-1", + }; + ("as_ref", method_anchor) + } + Mutability::Mut => ("as_mut", "as_mut"), + }; + err.span_note( + tcx.def_span(pick.item.def_id), + format!("the method `{item_name}` exists on the type `{ty}`", ty = pick.self_ty), ); + let mut_str = ptr_mutbl.ptr_str(); + err.note(format!( + "you might want to use the unsafe method `<*{mut_str} T>::{method}` to get \ + an optional reference to the value behind the pointer" + )); + err.note(format!( + "read the documentation for `<*{mut_str} T>::{method}` and ensure you satisfy its \ + safety preconditions before calling it to avoid undefined behavior: \ + https://doc.rust-lang.org/std/primitive.pointer.html#method.{method_anchor}" + )); } let mut ty_span = match rcvr_ty.kind() { diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index b17b312a797..94b723f694e 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -382,11 +382,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (err, output_def_id) } }; - if self.check_for_missing_semi(expr, &mut err) - && let hir::Node::Expr(expr) = self.tcx.parent_hir_node(expr.hir_id) - && let hir::ExprKind::Assign(..) = expr.kind + + // Try to suggest a semicolon if it's `A \n *B` where `B` is a place expr + let maybe_missing_semi = self.check_for_missing_semi(expr, &mut err); + + // We defer to the later error produced by `check_lhs_assignable`. + // We only downgrade this if it's the LHS, though. + if maybe_missing_semi + && let hir::Node::Expr(parent) = self.tcx.parent_hir_node(expr.hir_id) + && let hir::ExprKind::Assign(lhs, _, _) = parent.kind + && lhs.hir_id == expr.hir_id { - // We defer to the later error produced by `check_lhs_assignable`. err.downgrade_to_delayed_bug(); } diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index c987bfb9a0e..86f36eedd90 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -43,7 +43,8 @@ use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, Projection, Pro use rustc_middle::mir::FakeReadCause; use rustc_middle::traits::ObligationCauseCode; use rustc_middle::ty::{ - self, ClosureSizeProfileData, Ty, TyCtxt, TypeckResults, UpvarArgs, UpvarCapture, + self, ClosureSizeProfileData, Ty, TyCtxt, TypeVisitableExt as _, TypeckResults, UpvarArgs, + UpvarCapture, }; use rustc_session::lint; use rustc_span::sym; @@ -191,6 +192,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } }; + let args = self.resolve_vars_if_possible(args); let closure_def_id = closure_def_id.expect_local(); assert_eq!(self.tcx.hir().body_owner_def_id(body.id()), closure_def_id); @@ -361,43 +363,56 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // For coroutine-closures, we additionally must compute the // `coroutine_captures_by_ref_ty` type, which is used to generate the by-ref // version of the coroutine-closure's output coroutine. - if let UpvarArgs::CoroutineClosure(args) = args { + if let UpvarArgs::CoroutineClosure(args) = args + && !args.references_error() + { let closure_env_region: ty::Region<'_> = ty::Region::new_bound( self.tcx, ty::INNERMOST, ty::BoundRegion { var: ty::BoundVar::ZERO, kind: ty::BoundRegionKind::BrEnv }, ); + + let num_args = args + .as_coroutine_closure() + .coroutine_closure_sig() + .skip_binder() + .tupled_inputs_ty + .tuple_fields() + .len(); + let typeck_results = self.typeck_results.borrow(); + let tupled_upvars_ty_for_borrow = Ty::new_tup_from_iter( self.tcx, - self.typeck_results - .borrow() - .closure_min_captures_flattened( - self.tcx.coroutine_for_closure(closure_def_id).expect_local(), - ) - // Skip the captures that are just moving the closure's args - // into the coroutine. These are always by move, and we append - // those later in the `CoroutineClosureSignature` helper functions. - .skip( - args.as_coroutine_closure() - .coroutine_closure_sig() - .skip_binder() - .tupled_inputs_ty - .tuple_fields() - .len(), - ) - .map(|captured_place| { - let upvar_ty = captured_place.place.ty(); - let capture = captured_place.info.capture_kind; + ty::analyze_coroutine_closure_captures( + typeck_results.closure_min_captures_flattened(closure_def_id), + typeck_results + .closure_min_captures_flattened( + self.tcx.coroutine_for_closure(closure_def_id).expect_local(), + ) + // Skip the captures that are just moving the closure's args + // into the coroutine. These are always by move, and we append + // those later in the `CoroutineClosureSignature` helper functions. + .skip(num_args), + |(_, parent_capture), (_, child_capture)| { + // This is subtle. See documentation on function. + let needs_ref = should_reborrow_from_env_of_parent_coroutine_closure( + parent_capture, + child_capture, + ); + + let upvar_ty = child_capture.place.ty(); + let capture = child_capture.info.capture_kind; // Not all upvars are captured by ref, so use // `apply_capture_kind_on_capture_ty` to ensure that we // compute the right captured type. - apply_capture_kind_on_capture_ty( + return apply_capture_kind_on_capture_ty( self.tcx, upvar_ty, capture, - Some(closure_env_region), - ) - }), + if needs_ref { Some(closure_env_region) } else { child_capture.region }, + ); + }, + ), ); let coroutine_captures_by_ref_ty = Ty::new_fn_ptr( self.tcx, @@ -1761,6 +1776,63 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } +/// Determines whether a child capture that is derived from a parent capture +/// should be borrowed with the lifetime of the parent coroutine-closure's env. +/// +/// There are two cases when this needs to happen: +/// +/// (1.) Are we borrowing data owned by the parent closure? We can determine if +/// that is the case by checking if the parent capture is by move, EXCEPT if we +/// apply a deref projection, which means we're reborrowing a reference that we +/// captured by move. +/// +/// ```rust +/// #![feature(async_closure)] +/// let x = &1i32; // Let's call this lifetime `'1`. +/// let c = async move || { +/// println!("{:?}", *x); +/// // Even though the inner coroutine borrows by ref, we're only capturing `*x`, +/// // not `x`, so the inner closure is allowed to reborrow the data for `'1`. +/// }; +/// ``` +/// +/// (2.) If a coroutine is mutably borrowing from a parent capture, then that +/// mutable borrow cannot live for longer than either the parent *or* the borrow +/// that we have on the original upvar. Therefore we always need to borrow the +/// child capture with the lifetime of the parent coroutine-closure's env. +/// +/// ```rust +/// #![feature(async_closure)] +/// let mut x = 1i32; +/// let c = async || { +/// x = 1; +/// // The parent borrows `x` for some `&'1 mut i32`. +/// // However, when we call `c()`, we implicitly autoref for the signature of +/// // `AsyncFnMut::async_call_mut`. Let's call that lifetime `'call`. Since +/// // the maximum that `&'call mut &'1 mut i32` can be reborrowed is `&'call mut i32`, +/// // the inner coroutine should capture w/ the lifetime of the coroutine-closure. +/// }; +/// ``` +/// +/// If either of these cases apply, then we should capture the borrow with the +/// lifetime of the parent coroutine-closure's env. Luckily, if this function is +/// not correct, then the program is not unsound, since we still borrowck and validate +/// the choices made from this function -- the only side-effect is that the user +/// may receive unnecessary borrowck errors. +fn should_reborrow_from_env_of_parent_coroutine_closure<'tcx>( + parent_capture: &ty::CapturedPlace<'tcx>, + child_capture: &ty::CapturedPlace<'tcx>, +) -> bool { + // (1.) + (!parent_capture.is_by_ref() + && !matches!( + child_capture.place.projections.get(parent_capture.place.projections.len()), + Some(Projection { kind: ProjectionKind::Deref, .. }) + )) + // (2.) + || matches!(child_capture.info.capture_kind, UpvarCapture::ByRef(ty::BorrowKind::MutBorrow)) +} + /// Truncate the capture so that the place being borrowed is in accordance with RFC 1240, /// which states that it's unsafe to take a reference into a struct marked `repr(packed)`. fn restrict_repr_packed_field_ref_capture<'tcx>( diff --git a/compiler/rustc_infer/messages.ftl b/compiler/rustc_infer/messages.ftl index fdb6ab8f59b..64f52ea7ac1 100644 --- a/compiler/rustc_infer/messages.ftl +++ b/compiler/rustc_infer/messages.ftl @@ -144,6 +144,9 @@ infer_fps_items_are_distinct = fn items are distinct from fn pointers infer_fps_remove_ref = consider removing the reference infer_fps_use_ref = consider using a reference infer_fulfill_req_lifetime = the type `{$ty}` does not fulfill the required lifetime + +infer_full_type_written = the full type name has been written to '{$path}' + infer_implicit_static_lifetime_note = this has an implicit `'static` lifetime requirement infer_implicit_static_lifetime_suggestion = consider relaxing the implicit `'static` requirement infer_label_bad = {$bad_kind -> diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs index 6192eaf3c3a..4593108edac 100644 --- a/compiler/rustc_infer/src/errors/mod.rs +++ b/compiler/rustc_infer/src/errors/mod.rs @@ -18,6 +18,8 @@ use crate::infer::error_reporting::{ ObligationCauseAsDiagArg, }; +use std::path::PathBuf; + pub mod note_and_explain; #[derive(Diagnostic)] @@ -47,6 +49,9 @@ pub struct AnnotationRequired<'a> { pub infer_subdiags: Vec<SourceKindSubdiag<'a>>, #[subdiagnostic] pub multi_suggestions: Vec<SourceKindMultiSuggestion<'a>>, + #[note(infer_full_type_written)] + pub was_written: Option<()>, + pub path: PathBuf, } // Copy of `AnnotationRequired` for E0283 @@ -65,6 +70,9 @@ pub struct AmbiguousImpl<'a> { pub infer_subdiags: Vec<SourceKindSubdiag<'a>>, #[subdiagnostic] pub multi_suggestions: Vec<SourceKindMultiSuggestion<'a>>, + #[note(infer_full_type_written)] + pub was_written: Option<()>, + pub path: PathBuf, } // Copy of `AnnotationRequired` for E0284 @@ -83,6 +91,9 @@ pub struct AmbiguousReturn<'a> { pub infer_subdiags: Vec<SourceKindSubdiag<'a>>, #[subdiagnostic] pub multi_suggestions: Vec<SourceKindMultiSuggestion<'a>>, + #[note(infer_full_type_written)] + pub was_written: Option<()>, + pub path: PathBuf, } // Used when a better one isn't available diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs index f89ed256a08..3b5658ed0ee 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs @@ -18,13 +18,15 @@ use rustc_middle::infer::unify_key::{ }; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter, Print, Printer}; -use rustc_middle::ty::{self, InferConst}; -use rustc_middle::ty::{GenericArg, GenericArgKind, GenericArgsRef}; -use rustc_middle::ty::{IsSuggestable, Ty, TyCtxt, TypeckResults}; +use rustc_middle::ty::{ + self, GenericArg, GenericArgKind, GenericArgsRef, InferConst, IsSuggestable, Ty, TyCtxt, + TypeFoldable, TypeFolder, TypeSuperFoldable, TypeckResults, +}; use rustc_span::symbol::{kw, sym, Ident}; -use rustc_span::{BytePos, Span}; +use rustc_span::{BytePos, Span, DUMMY_SP}; use std::borrow::Cow; use std::iter; +use std::path::PathBuf; pub enum TypeAnnotationNeeded { /// ```compile_fail,E0282 @@ -153,6 +155,29 @@ impl UnderspecifiedArgKind { } } +struct ClosureEraser<'tcx> { + tcx: TyCtxt<'tcx>, +} + +impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ClosureEraser<'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + match ty.kind() { + ty::Closure(_, args) => { + let closure_sig = args.as_closure().sig(); + Ty::new_fn_ptr( + self.tcx, + self.tcx.signature_unclosure(closure_sig, hir::Unsafety::Normal), + ) + } + _ => ty.super_fold_with(self), + } + } +} + fn fmt_printer<'a, 'tcx>(infcx: &'a InferCtxt<'tcx>, ns: Namespace) -> FmtPrinter<'a, 'tcx> { let mut printer = FmtPrinter::new(infcx.tcx, ns); let ty_getter = move |ty_vid| { @@ -209,6 +234,10 @@ fn ty_to_string<'tcx>( ) -> String { let mut printer = fmt_printer(infcx, Namespace::TypeNS); let ty = infcx.resolve_vars_if_possible(ty); + // We use `fn` ptr syntax for closures, but this only works when the closure + // does not capture anything. + let ty = ty.fold_with(&mut ClosureEraser { tcx: infcx.tcx }); + match (ty.kind(), called_method_def_id) { // We don't want the regular output for `fn`s because it includes its path in // invalid pseudo-syntax, we want the `fn`-pointer output instead. @@ -223,11 +252,6 @@ fn ty_to_string<'tcx>( "Vec<_>".to_string() } _ if ty.is_ty_or_numeric_infer() => "/* Type */".to_string(), - // FIXME: The same thing for closures, but this only works when the closure - // does not capture anything. - // - // We do have to hide the `extern "rust-call"` ABI in that case though, - // which is too much of a bother for now. _ => { ty.print(&mut printer).unwrap(); printer.into_buffer() @@ -387,6 +411,8 @@ impl<'tcx> InferCtxt<'tcx> { infer_subdiags, multi_suggestions, bad_label, + was_written: None, + path: Default::default(), }), TypeAnnotationNeeded::E0283 => self.dcx().create_err(AmbiguousImpl { span, @@ -396,6 +422,8 @@ impl<'tcx> InferCtxt<'tcx> { infer_subdiags, multi_suggestions, bad_label, + was_written: None, + path: Default::default(), }), TypeAnnotationNeeded::E0284 => self.dcx().create_err(AmbiguousReturn { span, @@ -405,6 +433,8 @@ impl<'tcx> InferCtxt<'tcx> { infer_subdiags, multi_suggestions, bad_label, + was_written: None, + path: Default::default(), }), } } @@ -442,7 +472,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { return self.bad_inference_failure_err(failure_span, arg_data, error_code); }; - let (source_kind, name) = kind.ty_localized_msg(self); + let (source_kind, name, path) = kind.ty_localized_msg(self); let failure_span = if should_label_span && !failure_span.overlaps(span) { Some(failure_span) } else { @@ -518,7 +548,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { GenericArgKind::Lifetime(_) => bug!("unexpected lifetime"), GenericArgKind::Type(_) => self .next_ty_var(TypeVariableOrigin { - span: rustc_span::DUMMY_SP, + span: DUMMY_SP, kind: TypeVariableOriginKind::MiscVariable, }) .into(), @@ -526,7 +556,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { .next_const_var( arg.ty(), ConstVariableOrigin { - span: rustc_span::DUMMY_SP, + span: DUMMY_SP, kind: ConstVariableOriginKind::MiscVariable, }, ) @@ -547,7 +577,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } InferSourceKind::FullyQualifiedMethodCall { receiver, successor, args, def_id } => { let placeholder = Some(self.next_ty_var(TypeVariableOrigin { - span: rustc_span::DUMMY_SP, + span: DUMMY_SP, kind: TypeVariableOriginKind::MiscVariable, })); if let Some(args) = args.make_suggestable(self.infcx.tcx, true, placeholder) { @@ -584,7 +614,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } InferSourceKind::ClosureReturn { ty, data, should_wrap_expr } => { let placeholder = Some(self.next_ty_var(TypeVariableOrigin { - span: rustc_span::DUMMY_SP, + span: DUMMY_SP, kind: TypeVariableOriginKind::MiscVariable, })); if let Some(ty) = ty.make_suggestable(self.infcx.tcx, true, placeholder) { @@ -606,6 +636,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { infer_subdiags, multi_suggestions, bad_label: None, + was_written: path.as_ref().map(|_| ()), + path: path.unwrap_or_default(), }), TypeAnnotationNeeded::E0283 => self.dcx().create_err(AmbiguousImpl { span, @@ -615,6 +647,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { infer_subdiags, multi_suggestions, bad_label: None, + was_written: path.as_ref().map(|_| ()), + path: path.unwrap_or_default(), }), TypeAnnotationNeeded::E0284 => self.dcx().create_err(AmbiguousReturn { span, @@ -624,6 +658,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { infer_subdiags, multi_suggestions, bad_label: None, + was_written: path.as_ref().map(|_| ()), + path: path.unwrap_or_default(), }), } } @@ -688,22 +724,23 @@ impl<'tcx> InferSource<'tcx> { } impl<'tcx> InferSourceKind<'tcx> { - fn ty_localized_msg(&self, infcx: &InferCtxt<'tcx>) -> (&'static str, String) { + fn ty_localized_msg(&self, infcx: &InferCtxt<'tcx>) -> (&'static str, String, Option<PathBuf>) { + let mut path = None; match *self { InferSourceKind::LetBinding { ty, .. } | InferSourceKind::ClosureArg { ty, .. } | InferSourceKind::ClosureReturn { ty, .. } => { if ty.is_closure() { - ("closure", closure_as_fn_str(infcx, ty)) + ("closure", closure_as_fn_str(infcx, ty), path) } else if !ty.is_ty_or_numeric_infer() { - ("normal", ty_to_string(infcx, ty, None)) + ("normal", infcx.tcx.short_ty_string(ty, &mut path), path) } else { - ("other", String::new()) + ("other", String::new(), path) } } // FIXME: We should be able to add some additional info here. InferSourceKind::GenericArg { .. } - | InferSourceKind::FullyQualifiedMethodCall { .. } => ("other", String::new()), + | InferSourceKind::FullyQualifiedMethodCall { .. } => ("other", String::new(), path), } } } diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index d04d30b3cb0..91cef02c7d1 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -170,7 +170,9 @@ fn configure_and_expand( let mut old_path = OsString::new(); if cfg!(windows) { old_path = env::var_os("PATH").unwrap_or(old_path); - let mut new_path = sess.host_filesearch(PathKind::All).search_path_dirs(); + let mut new_path = Vec::from_iter( + sess.host_filesearch(PathKind::All).search_paths().map(|p| p.dir.clone()), + ); for path in env::split_paths(&old_path) { if !new_path.contains(&path) { new_path.push(path); diff --git a/compiler/rustc_lint/src/context/diagnostics.rs b/compiler/rustc_lint/src/context/diagnostics.rs index 16e3b8c11c1..b2403836397 100644 --- a/compiler/rustc_lint/src/context/diagnostics.rs +++ b/compiler/rustc_lint/src/context/diagnostics.rs @@ -105,7 +105,7 @@ pub(super) fn builtin(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Di BuiltinLintDiag::RedundantImport(spans, ident) => { for (span, is_imported) in spans { let introduced = if is_imported { "imported" } else { "defined" }; - let span_msg = if span.is_dummy() { "by prelude" } else { "here" }; + let span_msg = if span.is_dummy() { "by the extern prelude" } else { "here" }; diag.span_label( span, format!("the item `{ident}` is already {introduced} {span_msg}"), diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 111a4fdcea1..503caa35358 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -1074,10 +1074,14 @@ impl UnusedParens { // Otherwise proceed with linting. _ => {} } - let spans = inner - .span - .find_ancestor_inside(value.span) - .map(|inner| (value.span.with_hi(inner.lo()), value.span.with_lo(inner.hi()))); + let spans = if !value.span.from_expansion() { + inner + .span + .find_ancestor_inside(value.span) + .map(|inner| (value.span.with_hi(inner.lo()), value.span.with_lo(inner.hi()))) + } else { + None + }; self.emit_unused_delims(cx, value.span, spans, "pattern", keep_space, false); } } @@ -1233,11 +1237,13 @@ impl EarlyLintPass for UnusedParens { if self.with_self_ty_parens && b.generic_params.len() > 0 => {} ast::TyKind::ImplTrait(_, bounds) if bounds.len() > 1 => {} _ => { - let spans = r - .span - .find_ancestor_inside(ty.span) - .map(|r| (ty.span.with_hi(r.lo()), ty.span.with_lo(r.hi()))); - + let spans = if !ty.span.from_expansion() { + r.span + .find_ancestor_inside(ty.span) + .map(|r| (ty.span.with_hi(r.lo()), ty.span.with_lo(r.hi()))) + } else { + None + }; self.emit_unused_delims(cx, ty.span, spans, "type", (false, false), false); } } diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index baa2e1ff602..58760be921a 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -19,12 +19,7 @@ use crate::errors; use std::path::PathBuf; -pub fn find_native_static_library( - name: &str, - verbatim: bool, - search_paths: &[PathBuf], - sess: &Session, -) -> PathBuf { +pub fn find_native_static_library(name: &str, verbatim: bool, sess: &Session) -> PathBuf { let formats = if verbatim { vec![("".into(), "".into())] } else { @@ -35,9 +30,9 @@ pub fn find_native_static_library( if os == unix { vec![os] } else { vec![os, unix] } }; - for path in search_paths { + for path in sess.target_filesearch(PathKind::Native).search_paths() { for (prefix, suffix) in &formats { - let test = path.join(format!("{prefix}{name}{suffix}")); + let test = path.dir.join(format!("{prefix}{name}{suffix}")); if test.exists() { return test; } @@ -60,8 +55,7 @@ fn find_bundled_library( && (sess.opts.unstable_opts.packed_bundled_libs || has_cfg || whole_archive == Some(true)) { let verbatim = verbatim.unwrap_or(false); - let search_paths = &sess.target_filesearch(PathKind::Native).search_path_dirs(); - return find_native_static_library(name.as_str(), verbatim, search_paths, sess) + return find_native_static_library(name.as_str(), verbatim, sess) .file_name() .and_then(|s| s.to_str()) .map(Symbol::intern); diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 7b10ea53524..c057f7e921e 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -2009,7 +2009,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { .push((id.owner_id.def_id.local_def_index, simplified_self_ty)); let trait_def = tcx.trait_def(trait_ref.def_id); - if let Some(mut an) = trait_def.ancestors(tcx, def_id).ok() { + if let Ok(mut an) = trait_def.ancestors(tcx, def_id) { if let Some(specialization_graph::Node::Impl(parent)) = an.nth(1) { self.tables.impl_parent.set_some(def_id.index, parent.into()); } diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml index 9e8ee92b5d9..6c3ff237d59 100644 --- a/compiler/rustc_middle/Cargo.toml +++ b/compiler/rustc_middle/Cargo.toml @@ -10,7 +10,6 @@ derivative = "2.2.0" either = "1.5.0" field-offset = "0.3.5" gsgdt = "0.1.2" -measureme = "11" polonius-engine = "0.13.0" rustc-rayon = { version = "0.5.0", optional = true } rustc-rayon-core = { version = "0.5.0", optional = true } diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 41df2e3b587..15bd5c08965 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -978,12 +978,7 @@ impl<'tcx> Debug for Rvalue<'tcx> { CopyForDeref(ref place) => write!(fmt, "deref_copy {place:#?}"), AddressOf(mutability, ref place) => { - let kind_str = match mutability { - Mutability::Mut => "mut", - Mutability::Not => "const", - }; - - write!(fmt, "&raw {kind_str} {place:?}") + write!(fmt, "&raw {mut_str} {place:?}", mut_str = mutability.ptr_str()) } Aggregate(ref kind, ref places) => { diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs index c1548eb99f5..faa137019cb 100644 --- a/compiler/rustc_middle/src/query/keys.rs +++ b/compiler/rustc_middle/src/query/keys.rs @@ -555,6 +555,19 @@ impl Key for HirId { } } +impl Key for (LocalDefId, HirId) { + type Cache<V> = DefaultCache<Self, V>; + + fn default_span(&self, tcx: TyCtxt<'_>) -> Span { + tcx.hir().span(self.1) + } + + #[inline(always)] + fn key_as_def_id(&self) -> Option<DefId> { + Some(self.0.into()) + } +} + impl<'tcx> Key for (ValidityRequirement, ty::ParamEnvAnd<'tcx, Ty<'tcx>>) { type Cache<V> = DefaultCache<Self, V>; diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 5ef7a20f460..394515f091f 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -983,6 +983,9 @@ rustc_queries! { query diagnostic_only_typeck(key: LocalDefId) -> &'tcx ty::TypeckResults<'tcx> { desc { |tcx| "type-checking `{}`", tcx.def_path_str(key) } } + query lookup_method_for_diagnostic((def_id, hir_id): (LocalDefId, hir::HirId)) -> Option<DefId> { + desc { |tcx| "lookup_method_for_diagnostics `{}`", tcx.def_path_str(def_id) } + } query used_trait_imports(key: LocalDefId) -> &'tcx UnordSet<LocalDefId> { desc { |tcx| "finding used_trait_imports `{}`", tcx.def_path_str(key) } diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 6555a687152..4ce6f7747a5 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -8,8 +8,6 @@ use crate::query::{ }; use crate::ty::TyCtxt; use field_offset::FieldOffset; -use measureme::StringId; -use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::AtomicU64; use rustc_data_structures::sync::WorkerLocal; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -22,16 +20,6 @@ use rustc_query_system::HandleCycleError; use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP}; use std::ops::Deref; -pub struct QueryKeyStringCache { - pub def_id_cache: FxHashMap<DefId, StringId>, -} - -impl QueryKeyStringCache { - pub fn new() -> QueryKeyStringCache { - QueryKeyStringCache { def_id_cache: Default::default() } - } -} - pub struct DynamicQuery<'tcx, C: QueryCache> { pub name: &'static str, pub eval_always: bool, diff --git a/compiler/rustc_middle/src/ty/closure.rs b/compiler/rustc_middle/src/ty/closure.rs index 95d1e08b58b..211d403998f 100644 --- a/compiler/rustc_middle/src/ty/closure.rs +++ b/compiler/rustc_middle/src/ty/closure.rs @@ -6,6 +6,7 @@ use crate::{mir, ty}; use std::fmt::Write; use crate::query::Providers; +use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxIndexMap; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; @@ -415,6 +416,72 @@ impl BorrowKind { } } +pub fn analyze_coroutine_closure_captures<'a, 'tcx: 'a, T>( + parent_captures: impl IntoIterator<Item = &'a CapturedPlace<'tcx>>, + child_captures: impl IntoIterator<Item = &'a CapturedPlace<'tcx>>, + mut for_each: impl FnMut((usize, &'a CapturedPlace<'tcx>), (usize, &'a CapturedPlace<'tcx>)) -> T, +) -> impl Iterator<Item = T> + Captures<'a> + Captures<'tcx> { + std::iter::from_coroutine(move || { + let mut child_captures = child_captures.into_iter().enumerate().peekable(); + + // One parent capture may correspond to several child captures if we end up + // refining the set of captures via edition-2021 precise captures. We want to + // match up any number of child captures with one parent capture, so we keep + // peeking off this `Peekable` until the child doesn't match anymore. + for (parent_field_idx, parent_capture) in parent_captures.into_iter().enumerate() { + // Make sure we use every field at least once, b/c why are we capturing something + // if it's not used in the inner coroutine. + let mut field_used_at_least_once = false; + + // A parent matches a child if they share the same prefix of projections. + // The child may have more, if it is capturing sub-fields out of + // something that is captured by-move in the parent closure. + while child_captures.peek().map_or(false, |(_, child_capture)| { + child_prefix_matches_parent_projections(parent_capture, child_capture) + }) { + let (child_field_idx, child_capture) = child_captures.next().unwrap(); + // This analysis only makes sense if the parent capture is a + // prefix of the child capture. + assert!( + child_capture.place.projections.len() >= parent_capture.place.projections.len(), + "parent capture ({parent_capture:#?}) expected to be prefix of \ + child capture ({child_capture:#?})" + ); + + yield for_each( + (parent_field_idx, parent_capture), + (child_field_idx, child_capture), + ); + + field_used_at_least_once = true; + } + + // Make sure the field was used at least once. + assert!( + field_used_at_least_once, + "we captured {parent_capture:#?} but it was not used in the child coroutine?" + ); + } + assert_eq!(child_captures.next(), None, "leftover child captures?"); + }) +} + +fn child_prefix_matches_parent_projections( + parent_capture: &ty::CapturedPlace<'_>, + child_capture: &ty::CapturedPlace<'_>, +) -> bool { + let HirPlaceBase::Upvar(parent_base) = parent_capture.place.base else { + bug!("expected capture to be an upvar"); + }; + let HirPlaceBase::Upvar(child_base) = child_capture.place.base else { + bug!("expected capture to be an upvar"); + }; + + parent_base.var_path.hir_id == child_base.var_path.hir_id + && std::iter::zip(&child_capture.place.projections, &parent_capture.place.projections) + .all(|(child, parent)| child.kind == parent.kind) +} + pub fn provide(providers: &mut Providers) { *providers = Providers { closure_typeinfo, ..*providers } } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index ee4dc9744ac..e6b773ae512 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -77,9 +77,10 @@ pub use rustc_type_ir::ConstKind::{ pub use rustc_type_ir::*; pub use self::closure::{ - is_ancestor_or_same_capture, place_to_string_for_capture, BorrowKind, CaptureInfo, - CapturedPlace, ClosureTypeInfo, MinCaptureInformationMap, MinCaptureList, - RootVariableMinCaptureList, UpvarCapture, UpvarId, UpvarPath, CAPTURE_STRUCT_LOCAL, + analyze_coroutine_closure_captures, is_ancestor_or_same_capture, place_to_string_for_capture, + BorrowKind, CaptureInfo, CapturedPlace, ClosureTypeInfo, MinCaptureInformationMap, + MinCaptureList, RootVariableMinCaptureList, UpvarCapture, UpvarId, UpvarPath, + CAPTURE_STRUCT_LOCAL, }; pub use self::consts::{ Const, ConstData, ConstInt, ConstKind, Expr, ScalarInt, UnevaluatedConst, ValTree, diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 20ebd87c3d4..0bd009cd51d 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -671,13 +671,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { p!("(", print(ty), ") is ", write("{pat:?}")) } ty::RawPtr(ty, mutbl) => { - p!(write( - "*{} ", - match mutbl { - hir::Mutability::Mut => "mut", - hir::Mutability::Not => "const", - } - )); + p!(write("*{} ", mutbl.ptr_str())); p!(print(ty)) } ty::Ref(r, ty, mutbl) => { diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index dd73f0f4a35..ad64745d579 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -771,7 +771,7 @@ impl<'tcx> CoroutineArgs<'tcx> { } } -#[derive(Debug, Copy, Clone, HashStable)] +#[derive(Debug, Copy, Clone, HashStable, TypeFoldable, TypeVisitable)] pub enum UpvarArgs<'tcx> { Closure(GenericArgsRef<'tcx>), Coroutine(GenericArgsRef<'tcx>), diff --git a/compiler/rustc_middle/src/util/common.rs b/compiler/rustc_middle/src/util/common.rs index dd3a36c7bf8..2038d3f8448 100644 --- a/compiler/rustc_middle/src/util/common.rs +++ b/compiler/rustc_middle/src/util/common.rs @@ -1,8 +1,3 @@ -use rustc_data_structures::sync::Lock; - -use std::fmt::Debug; -use std::time::{Duration, Instant}; - #[cfg(test)] mod tests; @@ -26,46 +21,6 @@ pub fn to_readable_str(mut val: usize) -> String { groups.join("_") } -pub fn record_time<T, F>(accu: &Lock<Duration>, f: F) -> T -where - F: FnOnce() -> T, -{ - let start = Instant::now(); - let rv = f(); - let duration = start.elapsed(); - let mut accu = accu.lock(); - *accu += duration; - rv -} - -pub fn indent<R, F>(op: F) -> R -where - R: Debug, - F: FnOnce() -> R, -{ - // Use in conjunction with the log post-processor like `src/etc/indenter` - // to make debug output more readable. - debug!(">>"); - let r = op(); - debug!("<< (Result = {:?})", r); - r -} - -pub struct Indenter { - _cannot_construct_outside_of_this_module: (), -} - -impl Drop for Indenter { - fn drop(&mut self) { - debug!("<<"); - } -} - -pub fn indenter() -> Indenter { - debug!(">>"); - Indenter { _cannot_construct_outside_of_this_module: () } -} - // const wrapper for `if let Some((_, tail)) = name.rsplit_once(':') { tail } else { name }` pub const fn c_name(name: &'static str) -> &'static str { // FIXME Simplify the implementation once more `str` methods get const-stable. diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index a3e6c2abc51..03195a122b4 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -674,6 +674,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { if let Some(span) = sp && self.tcx.sess.source_map().is_span_accessible(span) && interpreted_as_const.is_none() + && scrut.is_some() { let mut bindings = vec![]; pat.each_binding(|name, _, _, _| bindings.push(name)); diff --git a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs index b26f968bf5e..3d6c1a95204 100644 --- a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs +++ b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs @@ -71,7 +71,7 @@ use rustc_data_structures::unord::UnordMap; use rustc_hir as hir; -use rustc_middle::hir::place::{PlaceBase, Projection, ProjectionKind}; +use rustc_middle::hir::place::{Projection, ProjectionKind}; use rustc_middle::mir::visit::MutVisitor; use rustc_middle::mir::{self, dump_mir, MirPass}; use rustc_middle::ty::{self, InstanceDef, Ty, TyCtxt, TypeVisitableExt}; @@ -124,44 +124,10 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody { .tuple_fields() .len(); - let mut field_remapping = UnordMap::default(); - - let mut child_captures = tcx - .closure_captures(coroutine_def_id) - .iter() - .copied() - // By construction we capture all the args first. - .skip(num_args) - .enumerate() - .peekable(); - - // One parent capture may correspond to several child captures if we end up - // refining the set of captures via edition-2021 precise captures. We want to - // match up any number of child captures with one parent capture, so we keep - // peeking off this `Peekable` until the child doesn't match anymore. - for (parent_field_idx, parent_capture) in - tcx.closure_captures(parent_def_id).iter().copied().enumerate() - { - // Make sure we use every field at least once, b/c why are we capturing something - // if it's not used in the inner coroutine. - let mut field_used_at_least_once = false; - - // A parent matches a child if they share the same prefix of projections. - // The child may have more, if it is capturing sub-fields out of - // something that is captured by-move in the parent closure. - while child_captures.peek().map_or(false, |(_, child_capture)| { - child_prefix_matches_parent_projections(parent_capture, child_capture) - }) { - let (child_field_idx, child_capture) = child_captures.next().unwrap(); - - // This analysis only makes sense if the parent capture is a - // prefix of the child capture. - assert!( - child_capture.place.projections.len() >= parent_capture.place.projections.len(), - "parent capture ({parent_capture:#?}) expected to be prefix of \ - child capture ({child_capture:#?})" - ); - + let field_remapping: UnordMap<_, _> = ty::analyze_coroutine_closure_captures( + tcx.closure_captures(parent_def_id).iter().copied(), + tcx.closure_captures(coroutine_def_id).iter().skip(num_args).copied(), + |(parent_field_idx, parent_capture), (child_field_idx, child_capture)| { // Store this set of additional projections (fields and derefs). // We need to re-apply them later. let child_precise_captures = @@ -192,7 +158,7 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody { ), }; - field_remapping.insert( + ( FieldIdx::from_usize(child_field_idx + num_args), ( FieldIdx::from_usize(parent_field_idx + num_args), @@ -200,18 +166,10 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody { needs_deref, child_precise_captures, ), - ); - - field_used_at_least_once = true; - } - - // Make sure the field was used at least once. - assert!( - field_used_at_least_once, - "we captured {parent_capture:#?} but it was not used in the child coroutine?" - ); - } - assert_eq!(child_captures.next(), None, "leftover child captures?"); + ) + }, + ) + .collect(); if coroutine_kind == ty::ClosureKind::FnOnce { assert_eq!(field_remapping.len(), tcx.closure_captures(parent_def_id).len()); @@ -241,22 +199,6 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody { } } -fn child_prefix_matches_parent_projections( - parent_capture: &ty::CapturedPlace<'_>, - child_capture: &ty::CapturedPlace<'_>, -) -> bool { - let PlaceBase::Upvar(parent_base) = parent_capture.place.base else { - bug!("expected capture to be an upvar"); - }; - let PlaceBase::Upvar(child_base) = child_capture.place.base else { - bug!("expected capture to be an upvar"); - }; - - parent_base.var_path.hir_id == child_base.var_path.hir_id - && std::iter::zip(&child_capture.place.projections, &parent_capture.place.projections) - .all(|(child, parent)| child.kind == parent.kind) -} - struct MakeByMoveBody<'tcx> { tcx: TyCtxt<'tcx>, field_remapping: UnordMap<FieldIdx, (FieldIdx, Ty<'tcx>, bool, &'tcx [Projection<'tcx>])>, diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 353c3f41ed8..1c1ca0bac81 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -697,7 +697,6 @@ impl<'psess, 'src> StringReader<'psess, 'src> { let expn_data = prefix_span.ctxt().outer_expn_data(); if expn_data.edition >= Edition::Edition2021 { - let mut silence = false; // In Rust 2021, this is a hard error. let sugg = if prefix == "rb" { Some(errors::UnknownPrefixSugg::UseBr(prefix_span)) @@ -705,25 +704,20 @@ impl<'psess, 'src> StringReader<'psess, 'src> { if self.cursor.first() == '\'' && let Some(start) = self.last_lifetime && self.cursor.third() != '\'' + && let end = self.mk_sp(self.pos, self.pos + BytePos(1)) + && !self.psess.source_map().is_multiline(start.until(end)) { - // An "unclosed `char`" error will be emitted already, silence redundant error. - silence = true; - Some(errors::UnknownPrefixSugg::MeantStr { - start, - end: self.mk_sp(self.pos, self.pos + BytePos(1)), - }) + // FIXME: An "unclosed `char`" error will be emitted already in some cases, + // but it's hard to silence this error while not also silencing important cases + // too. We should use the error stashing machinery instead. + Some(errors::UnknownPrefixSugg::MeantStr { start, end }) } else { Some(errors::UnknownPrefixSugg::Whitespace(prefix_span.shrink_to_hi())) } } else { None }; - let err = errors::UnknownPrefix { span: prefix_span, prefix, sugg }; - if silence { - self.dcx().create_err(err).delay_as_bug(); - } else { - self.dcx().emit_err(err); - } + self.dcx().emit_err(errors::UnknownPrefix { span: prefix_span, prefix, sugg }); } else { // Before Rust 2021, only emit a lint for migration. self.psess.buffer_lint_with_diagnostic( diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 012285e4644..00947a4c585 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -3585,8 +3585,7 @@ impl<'a> Parser<'a> { match self.expect_one_of(&[token::Comma], &[token::CloseDelim(close_delim)]) { Ok(_) => { - if let Some(f) = - parsed_field.or_else(|guar| field_ident(self, guar).ok_or(guar)).ok() + if let Ok(f) = parsed_field.or_else(|guar| field_ident(self, guar).ok_or(guar)) { // Only include the field if there's no parse error for the field name. fields.push(f); diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index 52767155532..3373835d813 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -13,6 +13,7 @@ extern crate rustc_middle; use crate::plumbing::{__rust_begin_short_backtrace, encode_all_query_results, try_mark_green}; +use crate::profiling_support::QueryKeyStringCache; use field_offset::offset_of; use rustc_data_structures::stable_hasher::HashStable; use rustc_data_structures::sync::AtomicU64; @@ -21,9 +22,7 @@ use rustc_middle::dep_graph::DepNodeIndex; use rustc_middle::dep_graph::{self, DepKind, DepKindStruct}; use rustc_middle::query::erase::{erase, restore, Erase}; use rustc_middle::query::on_disk_cache::{CacheEncoder, EncodedDepNodeIndex, OnDiskCache}; -use rustc_middle::query::plumbing::{ - DynamicQuery, QueryKeyStringCache, QuerySystem, QuerySystemFns, -}; +use rustc_middle::query::plumbing::{DynamicQuery, QuerySystem, QuerySystemFns}; use rustc_middle::query::AsLocalKey; use rustc_middle::query::{ queries, DynamicQueries, ExternProviders, Providers, QueryCaches, QueryEngine, QueryStates, diff --git a/compiler/rustc_query_impl/src/profiling_support.rs b/compiler/rustc_query_impl/src/profiling_support.rs index fbc6db93e01..e0d7a4f0451 100644 --- a/compiler/rustc_query_impl/src/profiling_support.rs +++ b/compiler/rustc_query_impl/src/profiling_support.rs @@ -1,13 +1,23 @@ use measureme::{StringComponent, StringId}; +use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::profiling::SelfProfiler; use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, LOCAL_CRATE}; use rustc_hir::definitions::DefPathData; -use rustc_middle::query::plumbing::QueryKeyStringCache; use rustc_middle::ty::TyCtxt; use rustc_query_system::query::QueryCache; use std::fmt::Debug; use std::io::Write; +pub(crate) struct QueryKeyStringCache { + def_id_cache: FxHashMap<DefId, StringId>, +} + +impl QueryKeyStringCache { + fn new() -> QueryKeyStringCache { + QueryKeyStringCache { def_id_cache: Default::default() } + } +} + struct QueryKeyStringBuilder<'p, 'tcx> { profiler: &'p SelfProfiler, tcx: TyCtxt<'tcx>, diff --git a/compiler/rustc_query_system/src/dep_graph/serialized.rs b/compiler/rustc_query_system/src/dep_graph/serialized.rs index 2bc7cb99547..6042aa6a2d2 100644 --- a/compiler/rustc_query_system/src/dep_graph/serialized.rs +++ b/compiler/rustc_query_system/src/dep_graph/serialized.rs @@ -541,12 +541,7 @@ impl<D: Deps> EncoderState<D> { record_graph: &Option<Lock<DepGraphQuery>>, ) -> DepNodeIndex { node.encode::<D>(&mut self.encoder); - self.record( - node.node, - node.edges.len(), - |_| node.edges[..].iter().copied().collect(), - record_graph, - ) + self.record(node.node, node.edges.len(), |_| node.edges[..].to_vec(), record_graph) } /// Encodes a node that was promoted from the previous graph. It reads the information directly from diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 76fe36a77cb..3d9380a3ebd 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -1391,6 +1391,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let mut redundant_spans: Vec<_> = redundant_span.present_items().collect(); redundant_spans.sort(); redundant_spans.dedup(); + /* FIXME(unused_imports): Add this back as a new lint self.lint_buffer.buffer_lint_with_diagnostic( UNUSED_IMPORTS, id, @@ -1398,6 +1399,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { format!("the item `{source}` is imported redundantly"), BuiltinLintDiag::RedundantImport(redundant_spans, source), ); + */ return true; } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 39ccf6d3714..b8221d9d7f9 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -177,7 +177,7 @@ enum ImplTraitContext { /// Used for tracking import use types which will be used for redundant import checking. /// ### Used::Scope Example -/// ```rust,compile_fail +/// ```rust,ignore (redundant_imports) /// #![deny(unused_imports)] /// use std::mem::drop; /// fn main() { diff --git a/compiler/rustc_resolve/src/rustdoc.rs b/compiler/rustc_resolve/src/rustdoc.rs index 0ebcad3cdb8..0bc7579918c 100644 --- a/compiler/rustc_resolve/src/rustdoc.rs +++ b/compiler/rustc_resolve/src/rustdoc.rs @@ -194,12 +194,12 @@ pub fn attrs_to_doc_fragments<'a>( for (attr, item_id) in attrs { if let Some((doc_str, comment_kind)) = attr.doc_str_and_comment_kind() { let doc = beautify_doc_string(doc_str, comment_kind); - let kind = if attr.is_doc_comment() { - DocFragmentKind::SugaredDoc + let (span, kind) = if attr.is_doc_comment() { + (attr.span, DocFragmentKind::SugaredDoc) } else { - DocFragmentKind::RawDoc + (span_for_value(attr), DocFragmentKind::RawDoc) }; - let fragment = DocFragment { span: attr.span, doc, kind, item_id, indent: 0 }; + let fragment = DocFragment { span, doc, kind, item_id, indent: 0 }; doc_fragments.push(fragment); } else if !doc_only { other_attrs.push(attr.clone()); @@ -211,6 +211,16 @@ pub fn attrs_to_doc_fragments<'a>( (doc_fragments, other_attrs) } +fn span_for_value(attr: &ast::Attribute) -> Span { + if let ast::AttrKind::Normal(normal) = &attr.kind + && let ast::AttrArgs::Eq(_, ast::AttrArgsEq::Hir(meta)) = &normal.item.args + { + meta.span.with_ctxt(attr.span.ctxt()) + } else { + attr.span + } +} + /// Return the doc-comments on this item, grouped by the module they came from. /// The module can be different if this is a re-export with added documentation. /// @@ -482,15 +492,36 @@ pub fn span_of_fragments(fragments: &[DocFragment]) -> Option<Span> { /// Attempts to match a range of bytes from parsed markdown to a `Span` in the source code. /// -/// This method will return `None` if we cannot construct a span from the source map or if the -/// fragments are not all sugared doc comments. It's difficult to calculate the correct span in -/// that case due to escaping and other source features. +/// This method does not always work, because markdown bytes don't necessarily match source bytes, +/// like if escapes are used in the string. In this case, it returns `None`. +/// +/// This method will return `Some` only if: +/// +/// - The doc is made entirely from sugared doc comments, which cannot contain escapes +/// - The doc is entirely from a single doc fragment, with a string literal, exactly equal +/// - The doc comes from `include_str!` pub fn source_span_for_markdown_range( tcx: TyCtxt<'_>, markdown: &str, md_range: &Range<usize>, fragments: &[DocFragment], ) -> Option<Span> { + if let &[fragment] = &fragments + && fragment.kind == DocFragmentKind::RawDoc + && let Ok(snippet) = tcx.sess.source_map().span_to_snippet(fragment.span) + && snippet.trim_end() == markdown.trim_end() + && let Ok(md_range_lo) = u32::try_from(md_range.start) + && let Ok(md_range_hi) = u32::try_from(md_range.end) + { + // Single fragment with string that contains same bytes as doc. + return Some(Span::new( + fragment.span.lo() + rustc_span::BytePos(md_range_lo), + fragment.span.lo() + rustc_span::BytePos(md_range_hi), + fragment.span.ctxt(), + fragment.span.parent(), + )); + } + let is_all_sugared_doc = fragments.iter().all(|frag| frag.kind == DocFragmentKind::SugaredDoc); if !is_all_sugared_doc { diff --git a/compiler/rustc_serialize/src/int_overflow.rs b/compiler/rustc_serialize/src/int_overflow.rs new file mode 100644 index 00000000000..f2aac2ef711 --- /dev/null +++ b/compiler/rustc_serialize/src/int_overflow.rs @@ -0,0 +1,65 @@ +// This would belong to `rustc_data_structures`, but `rustc_serialize` needs it too. + +/// Addition, but only overflow checked when `cfg(debug_assertions)` is set +/// instead of respecting `-Coverflow-checks`. +/// +/// This exists for performance reasons, as we ship rustc with overflow checks. +/// While overflow checks are perf neutral in almost all of the compiler, there +/// are a few particularly hot areas where we don't want overflow checks in our +/// dist builds. Overflow is still a bug there, so we want overflow check for +/// builds with debug assertions. +/// +/// That's a long way to say that this should be used in areas where overflow +/// is a bug but overflow checking is too slow. +pub trait DebugStrictAdd { + /// See [`DebugStrictAdd`]. + fn debug_strict_add(self, other: Self) -> Self; +} + +macro_rules! impl_debug_strict_add { + ($( $ty:ty )*) => { + $( + impl DebugStrictAdd for $ty { + fn debug_strict_add(self, other: Self) -> Self { + if cfg!(debug_assertions) { + self + other + } else { + self.wrapping_add(other) + } + } + } + )* + }; +} + +/// See [`DebugStrictAdd`]. +pub trait DebugStrictSub { + /// See [`DebugStrictAdd`]. + fn debug_strict_sub(self, other: Self) -> Self; +} + +macro_rules! impl_debug_strict_sub { + ($( $ty:ty )*) => { + $( + impl DebugStrictSub for $ty { + fn debug_strict_sub(self, other: Self) -> Self { + if cfg!(debug_assertions) { + self - other + } else { + self.wrapping_sub(other) + } + } + } + )* + }; +} + +impl_debug_strict_add! { + u8 u16 u32 u64 u128 usize + i8 i16 i32 i64 i128 isize +} + +impl_debug_strict_sub! { + u8 u16 u32 u64 u128 usize + i8 i16 i32 i64 i128 isize +} diff --git a/compiler/rustc_serialize/src/leb128.rs b/compiler/rustc_serialize/src/leb128.rs index ca661bac78c..44324804468 100644 --- a/compiler/rustc_serialize/src/leb128.rs +++ b/compiler/rustc_serialize/src/leb128.rs @@ -1,6 +1,10 @@ use crate::opaque::MemDecoder; use crate::serialize::Decoder; +// This code is very hot and uses lots of arithmetic, avoid overflow checks for performance. +// See https://github.com/rust-lang/rust/pull/119440#issuecomment-1874255727 +use crate::int_overflow::DebugStrictAdd; + /// Returns the length of the longest LEB128 encoding for `T`, assuming `T` is an integer type pub const fn max_leb128_len<T>() -> usize { // The longest LEB128 encoding for an integer uses 7 bits per byte. @@ -24,7 +28,7 @@ macro_rules! impl_write_unsigned_leb128 { *out.get_unchecked_mut(i) = value as u8; } - i += 1; + i = i.debug_strict_add(1); break; } else { unsafe { @@ -32,7 +36,7 @@ macro_rules! impl_write_unsigned_leb128 { } value >>= 7; - i += 1; + i = i.debug_strict_add(1); } } @@ -69,7 +73,7 @@ macro_rules! impl_read_unsigned_leb128 { } else { result |= ((byte & 0x7F) as $int_ty) << shift; } - shift += 7; + shift = shift.debug_strict_add(7); } } }; @@ -101,7 +105,7 @@ macro_rules! impl_write_signed_leb128 { *out.get_unchecked_mut(i) = byte; } - i += 1; + i = i.debug_strict_add(1); if !more { break; @@ -130,7 +134,7 @@ macro_rules! impl_read_signed_leb128 { loop { byte = decoder.read_u8(); result |= <$int_ty>::from(byte & 0x7F) << shift; - shift += 7; + shift = shift.debug_strict_add(7); if (byte & 0x80) == 0 { break; diff --git a/compiler/rustc_serialize/src/lib.rs b/compiler/rustc_serialize/src/lib.rs index b67b7d79d97..5a9403e0a85 100644 --- a/compiler/rustc_serialize/src/lib.rs +++ b/compiler/rustc_serialize/src/lib.rs @@ -23,5 +23,6 @@ pub use self::serialize::{Decodable, Decoder, Encodable, Encoder}; mod serialize; +pub mod int_overflow; pub mod leb128; pub mod opaque; diff --git a/compiler/rustc_serialize/src/opaque.rs b/compiler/rustc_serialize/src/opaque.rs index cc8d1c25092..eec83c02d35 100644 --- a/compiler/rustc_serialize/src/opaque.rs +++ b/compiler/rustc_serialize/src/opaque.rs @@ -7,6 +7,10 @@ use std::ops::Range; use std::path::Path; use std::path::PathBuf; +// This code is very hot and uses lots of arithmetic, avoid overflow checks for performance. +// See https://github.com/rust-lang/rust/pull/119440#issuecomment-1874255727 +use crate::int_overflow::DebugStrictAdd; + // ----------------------------------------------------------------------------- // Encoder // ----------------------------------------------------------------------------- @@ -65,7 +69,7 @@ impl FileEncoder { // Tracking position this way instead of having a `self.position` field // means that we only need to update `self.buffered` on a write call, // as opposed to updating `self.position` and `self.buffered`. - self.flushed + self.buffered + self.flushed.debug_strict_add(self.buffered) } #[cold] @@ -119,7 +123,7 @@ impl FileEncoder { } if let Some(dest) = self.buffer_empty().get_mut(..buf.len()) { dest.copy_from_slice(buf); - self.buffered += buf.len(); + self.buffered = self.buffered.debug_strict_add(buf.len()); } else { self.write_all_cold_path(buf); } @@ -158,7 +162,7 @@ impl FileEncoder { if written > N { Self::panic_invalid_write::<N>(written); } - self.buffered += written; + self.buffered = self.buffered.debug_strict_add(written); } #[cold] diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index a4919b25fe3..34acb4ea10f 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -18,7 +18,7 @@ use rustc_feature::UnstableFeatures; use rustc_span::edition::{Edition, DEFAULT_EDITION, EDITION_NAME_LIST, LATEST_STABLE_EDITION}; use rustc_span::source_map::FilePathMapping; use rustc_span::{FileName, FileNameDisplayPreference, RealFileName, SourceFileHashAlgorithm}; -use rustc_target::spec::LinkSelfContainedComponents; +use rustc_target::spec::{LinkSelfContainedComponents, LinkerFeatures}; use rustc_target::spec::{SplitDebuginfo, Target, TargetTriple}; use std::collections::btree_map::{ Iter as BTreeMapIter, Keys as BTreeMapKeysIter, Values as BTreeMapValuesIter, @@ -292,6 +292,48 @@ impl LinkSelfContained { } } +/// The different values that `-Z linker-features` can take on the CLI: a list of individually +/// enabled or disabled features used during linking. +/// +/// There is no need to enable or disable them in bulk. Each feature is fine-grained, and can be +/// used to turn `LinkerFeatures` on or off, without needing to change the linker flavor: +/// - using the system lld, or the self-contained `rust-lld` linker +/// - using a C/C++ compiler to drive the linker (not yet exposed on the CLI) +/// - etc. +#[derive(Default, Copy, Clone, PartialEq, Debug)] +pub struct LinkerFeaturesCli { + /// The linker features that are enabled on the CLI, using the `+feature` syntax. + pub enabled: LinkerFeatures, + + /// The linker features that are disabled on the CLI, using the `-feature` syntax. + pub disabled: LinkerFeatures, +} + +impl LinkerFeaturesCli { + /// Accumulates an enabled or disabled feature as specified on the CLI, if possible. + /// For example: `+lld`, and `-lld`. + pub(crate) fn handle_cli_feature(&mut self, feature: &str) -> Option<()> { + // Duplicate flags are reduced as we go, the last occurrence wins: + // `+feature,-feature,+feature` only enables the feature, and does not record it as both + // enabled and disabled on the CLI. + // We also only expose `+/-lld` at the moment, as it's currenty the only implemented linker + // feature and toggling `LinkerFeatures::CC` would be a noop. + match feature { + "+lld" => { + self.enabled.insert(LinkerFeatures::LLD); + self.disabled.remove(LinkerFeatures::LLD); + Some(()) + } + "-lld" => { + self.disabled.insert(LinkerFeatures::LLD); + self.enabled.remove(LinkerFeatures::LLD); + Some(()) + } + _ => None, + } + } +} + /// Used with `-Z assert-incr-state`. #[derive(Clone, Copy, PartialEq, Hash, Debug)] pub enum IncrementalStateAssertion { diff --git a/compiler/rustc_session/src/filesearch.rs b/compiler/rustc_session/src/filesearch.rs index 4f0e3354680..aecf5954c4c 100644 --- a/compiler/rustc_session/src/filesearch.rs +++ b/compiler/rustc_session/src/filesearch.rs @@ -45,11 +45,6 @@ impl<'a> FileSearch<'a> { debug!("using sysroot = {}, triple = {}", sysroot.display(), triple); FileSearch { sysroot, triple, search_paths, tlib_path, kind } } - - /// Returns just the directories within the search paths. - pub fn search_path_dirs(&self) -> Vec<PathBuf> { - self.search_paths().map(|sp| sp.dir.to_path_buf()).collect() - } } pub fn make_target_lib_path(sysroot: &Path, target_triple: &str) -> PathBuf { diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 5e7c2465097..963c9558c17 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -426,6 +426,8 @@ mod desc { "one of supported split dwarf modes (`split` or `single`)"; pub const parse_link_self_contained: &str = "one of: `y`, `yes`, `on`, `n`, `no`, `off`, or a list of enabled (`+` prefix) and disabled (`-` prefix) \ components: `crto`, `libc`, `unwind`, `linker`, `sanitizers`, `mingw`"; + pub const parse_linker_features: &str = + "a list of enabled (`+` prefix) and disabled (`-` prefix) features: `lld`"; pub const parse_polonius: &str = "either no value or `legacy` (the default), or `next`"; pub const parse_stack_protector: &str = "one of (`none` (default), `basic`, `strong`, or `all`)"; @@ -1269,6 +1271,22 @@ mod parse { true } + /// Parse a comma-separated list of enabled and disabled linker features. + pub(crate) fn parse_linker_features(slot: &mut LinkerFeaturesCli, v: Option<&str>) -> bool { + match v { + Some(s) => { + for feature in s.split(',') { + if slot.handle_cli_feature(feature).is_none() { + return false; + } + } + + true + } + None => false, + } + } + pub(crate) fn parse_wasi_exec_model(slot: &mut Option<WasiExecModel>, v: Option<&str>) -> bool { match v { Some("command") => *slot = Some(WasiExecModel::Command), @@ -1721,6 +1739,8 @@ options! { "link native libraries in the linker invocation (default: yes)"), link_only: bool = (false, parse_bool, [TRACKED], "link the `.rlink` file generated by `-Z no-link` (default: no)"), + linker_features: LinkerFeaturesCli = (LinkerFeaturesCli::default(), parse_linker_features, [UNTRACKED], + "a comma-separated list of linker features to enable (+) or disable (-): `lld`"), lint_mir: bool = (false, parse_bool, [UNTRACKED], "lint MIR before and after each transformation"), llvm_module_flag: Vec<(String, u32, String)> = (Vec::new(), parse_llvm_module_flag, [TRACKED], diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 7ce879807ca..c1e1175b4bd 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -743,6 +743,45 @@ impl Span { Some(self) } + /// Recursively walk down the expansion ancestors to find the oldest ancestor span with the same + /// [`SyntaxContext`] the initial span. + /// + /// This method is suitable for peeling through *local* macro expansions to find the "innermost" + /// span that is still local and shares the same [`SyntaxContext`]. For example, given + /// + /// ```ignore (illustrative example, contains type error) + /// macro_rules! outer { + /// ($x: expr) => { + /// inner!($x) + /// } + /// } + /// + /// macro_rules! inner { + /// ($x: expr) => { + /// format!("error: {}", $x) + /// //~^ ERROR mismatched types + /// } + /// } + /// + /// fn bar(x: &str) -> Result<(), Box<dyn std::error::Error>> { + /// Err(outer!(x)) + /// } + /// ``` + /// + /// if provided the initial span of `outer!(x)` inside `bar`, this method will recurse + /// the parent callsites until we reach `format!("error: {}", $x)`, at which point it is the + /// oldest ancestor span that is both still local and shares the same [`SyntaxContext`] as the + /// initial span. + pub fn find_oldest_ancestor_in_same_ctxt(self) -> Span { + let mut cur = self; + while cur.eq_ctxt(self) + && let Some(parent_callsite) = cur.parent_callsite() + { + cur = parent_callsite; + } + cur + } + /// Edition of the crate from which this span came. pub fn edition(self) -> edition::Edition { self.ctxt().edition() diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index f721a04d6b9..93d5f06a167 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -218,7 +218,7 @@ impl SourceMap { /// /// Unlike `load_file`, guarantees that no normalization like BOM-removal /// takes place. - pub fn load_binary_file(&self, path: &Path) -> io::Result<Lrc<[u8]>> { + pub fn load_binary_file(&self, path: &Path) -> io::Result<(Lrc<[u8]>, Span)> { let bytes = self.file_loader.read_binary_file(path)?; // We need to add file to the `SourceMap`, so that it is present @@ -227,8 +227,16 @@ impl SourceMap { // via `mod`, so we try to use real file contents and not just an // empty string. let text = std::str::from_utf8(&bytes).unwrap_or("").to_string(); - self.new_source_file(path.to_owned().into(), text); - Ok(bytes) + let file = self.new_source_file(path.to_owned().into(), text); + Ok(( + bytes, + Span::new( + file.start_pos, + BytePos(file.start_pos.0 + file.source_len.0), + SyntaxContext::root(), + None, + ), + )) } // By returning a `MonotonicVec`, we ensure that consumers cannot invalidate diff --git a/compiler/rustc_span/src/span_encoding.rs b/compiler/rustc_span/src/span_encoding.rs index e162695a13b..788a52faf56 100644 --- a/compiler/rustc_span/src/span_encoding.rs +++ b/compiler/rustc_span/src/span_encoding.rs @@ -5,6 +5,10 @@ use crate::{BytePos, SpanData}; use rustc_data_structures::fx::FxIndexSet; +// This code is very hot and uses lots of arithmetic, avoid overflow checks for performance. +// See https://github.com/rust-lang/rust/pull/119440#issuecomment-1874255727 +use rustc_serialize::int_overflow::DebugStrictAdd; + /// A compressed span. /// /// [`SpanData`] is 16 bytes, which is too big to stick everywhere. `Span` only @@ -166,7 +170,7 @@ impl Span { debug_assert!(len <= MAX_LEN); SpanData { lo: BytePos(self.lo_or_index), - hi: BytePos(self.lo_or_index + len), + hi: BytePos(self.lo_or_index.debug_strict_add(len)), ctxt: SyntaxContext::from_u32(self.ctxt_or_parent_or_marker as u32), parent: None, } @@ -179,7 +183,7 @@ impl Span { }; SpanData { lo: BytePos(self.lo_or_index), - hi: BytePos(self.lo_or_index + len), + hi: BytePos(self.lo_or_index.debug_strict_add(len)), ctxt: SyntaxContext::root(), parent: Some(parent), } diff --git a/compiler/rustc_target/src/asm/aarch64.rs b/compiler/rustc_target/src/asm/aarch64.rs index 70528c1222c..5ae9a2e2058 100644 --- a/compiler/rustc_target/src/asm/aarch64.rs +++ b/compiler/rustc_target/src/asm/aarch64.rs @@ -87,6 +87,20 @@ fn reserved_x18( } } +fn restricted_for_arm64ec( + arch: InlineAsmArch, + _reloc_model: RelocModel, + _target_features: &FxIndexSet<Symbol>, + _target: &Target, + _is_clobber: bool, +) -> Result<(), &'static str> { + if arch == InlineAsmArch::Arm64EC { + Err("x13, x14, x23, x24, x28, v16-v31 cannot be used for Arm64EC") + } else { + Ok(()) + } +} + def_regs! { AArch64 AArch64InlineAsmReg AArch64InlineAsmRegClass { x0: reg = ["x0", "w0"], @@ -102,8 +116,8 @@ def_regs! { x10: reg = ["x10", "w10"], x11: reg = ["x11", "w11"], x12: reg = ["x12", "w12"], - x13: reg = ["x13", "w13"], - x14: reg = ["x14", "w14"], + x13: reg = ["x13", "w13"] % restricted_for_arm64ec, + x14: reg = ["x14", "w14"] % restricted_for_arm64ec, x15: reg = ["x15", "w15"], x16: reg = ["x16", "w16"], x17: reg = ["x17", "w17"], @@ -111,12 +125,12 @@ def_regs! { x20: reg = ["x20", "w20"], x21: reg = ["x21", "w21"], x22: reg = ["x22", "w22"], - x23: reg = ["x23", "w23"], - x24: reg = ["x24", "w24"], + x23: reg = ["x23", "w23"] % restricted_for_arm64ec, + x24: reg = ["x24", "w24"] % restricted_for_arm64ec, x25: reg = ["x25", "w25"], x26: reg = ["x26", "w26"], x27: reg = ["x27", "w27"], - x28: reg = ["x28", "w28"], + x28: reg = ["x28", "w28"] % restricted_for_arm64ec, x30: reg = ["x30", "w30", "lr", "wlr"], v0: vreg, vreg_low16 = ["v0", "b0", "h0", "s0", "d0", "q0", "z0"], v1: vreg, vreg_low16 = ["v1", "b1", "h1", "s1", "d1", "q1", "z1"], @@ -134,22 +148,22 @@ def_regs! { v13: vreg, vreg_low16 = ["v13", "b13", "h13", "s13", "d13", "q13", "z13"], v14: vreg, vreg_low16 = ["v14", "b14", "h14", "s14", "d14", "q14", "z14"], v15: vreg, vreg_low16 = ["v15", "b15", "h15", "s15", "d15", "q15", "z15"], - v16: vreg = ["v16", "b16", "h16", "s16", "d16", "q16", "z16"], - v17: vreg = ["v17", "b17", "h17", "s17", "d17", "q17", "z17"], - v18: vreg = ["v18", "b18", "h18", "s18", "d18", "q18", "z18"], - v19: vreg = ["v19", "b19", "h19", "s19", "d19", "q19", "z19"], - v20: vreg = ["v20", "b20", "h20", "s20", "d20", "q20", "z20"], - v21: vreg = ["v21", "b21", "h21", "s21", "d21", "q21", "z21"], - v22: vreg = ["v22", "b22", "h22", "s22", "d22", "q22", "z22"], - v23: vreg = ["v23", "b23", "h23", "s23", "d23", "q23", "z23"], - v24: vreg = ["v24", "b24", "h24", "s24", "d24", "q24", "z24"], - v25: vreg = ["v25", "b25", "h25", "s25", "d25", "q25", "z25"], - v26: vreg = ["v26", "b26", "h26", "s26", "d26", "q26", "z26"], - v27: vreg = ["v27", "b27", "h27", "s27", "d27", "q27", "z27"], - v28: vreg = ["v28", "b28", "h28", "s28", "d28", "q28", "z28"], - v29: vreg = ["v29", "b29", "h29", "s29", "d29", "q29", "z29"], - v30: vreg = ["v30", "b30", "h30", "s30", "d30", "q30", "z30"], - v31: vreg = ["v31", "b31", "h31", "s31", "d31", "q31", "z31"], + v16: vreg = ["v16", "b16", "h16", "s16", "d16", "q16", "z16"] % restricted_for_arm64ec, + v17: vreg = ["v17", "b17", "h17", "s17", "d17", "q17", "z17"] % restricted_for_arm64ec, + v18: vreg = ["v18", "b18", "h18", "s18", "d18", "q18", "z18"] % restricted_for_arm64ec, + v19: vreg = ["v19", "b19", "h19", "s19", "d19", "q19", "z19"] % restricted_for_arm64ec, + v20: vreg = ["v20", "b20", "h20", "s20", "d20", "q20", "z20"] % restricted_for_arm64ec, + v21: vreg = ["v21", "b21", "h21", "s21", "d21", "q21", "z21"] % restricted_for_arm64ec, + v22: vreg = ["v22", "b22", "h22", "s22", "d22", "q22", "z22"] % restricted_for_arm64ec, + v23: vreg = ["v23", "b23", "h23", "s23", "d23", "q23", "z23"] % restricted_for_arm64ec, + v24: vreg = ["v24", "b24", "h24", "s24", "d24", "q24", "z24"] % restricted_for_arm64ec, + v25: vreg = ["v25", "b25", "h25", "s25", "d25", "q25", "z25"] % restricted_for_arm64ec, + v26: vreg = ["v26", "b26", "h26", "s26", "d26", "q26", "z26"] % restricted_for_arm64ec, + v27: vreg = ["v27", "b27", "h27", "s27", "d27", "q27", "z27"] % restricted_for_arm64ec, + v28: vreg = ["v28", "b28", "h28", "s28", "d28", "q28", "z28"] % restricted_for_arm64ec, + v29: vreg = ["v29", "b29", "h29", "s29", "d29", "q29", "z29"] % restricted_for_arm64ec, + v30: vreg = ["v30", "b30", "h30", "s30", "d30", "q30", "z30"] % restricted_for_arm64ec, + v31: vreg = ["v31", "b31", "h31", "s31", "d31", "q31", "z31"] % restricted_for_arm64ec, p0: preg = ["p0"], p1: preg = ["p1"], p2: preg = ["p2"], diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs index 2e04dca98c5..49de92b86cb 100644 --- a/compiler/rustc_target/src/asm/mod.rs +++ b/compiler/rustc_target/src/asm/mod.rs @@ -9,11 +9,11 @@ use std::str::FromStr; pub struct ModifierInfo { pub modifier: char, pub result: &'static str, - pub size: u64, + pub size: u16, } -impl From<(char, &'static str, u64)> for ModifierInfo { - fn from((modifier, result, size): (char, &'static str, u64)) -> Self { +impl From<(char, &'static str, u16)> for ModifierInfo { + fn from((modifier, result, size): (char, &'static str, u16)) -> Self { Self { modifier, result, size } } } @@ -217,6 +217,7 @@ pub enum InlineAsmArch { X86_64, Arm, AArch64, + Arm64EC, RiscV32, RiscV64, Nvptx64, @@ -246,6 +247,7 @@ impl FromStr for InlineAsmArch { "x86_64" => Ok(Self::X86_64), "arm" => Ok(Self::Arm), "aarch64" => Ok(Self::AArch64), + "arm64ec" => Ok(Self::Arm64EC), "riscv32" => Ok(Self::RiscV32), "riscv64" => Ok(Self::RiscV64), "nvptx64" => Ok(Self::Nvptx64), @@ -341,7 +343,9 @@ impl InlineAsmReg { Ok(match arch { InlineAsmArch::X86 | InlineAsmArch::X86_64 => Self::X86(X86InlineAsmReg::parse(name)?), InlineAsmArch::Arm => Self::Arm(ArmInlineAsmReg::parse(name)?), - InlineAsmArch::AArch64 => Self::AArch64(AArch64InlineAsmReg::parse(name)?), + InlineAsmArch::AArch64 | InlineAsmArch::Arm64EC => { + Self::AArch64(AArch64InlineAsmReg::parse(name)?) + } InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => { Self::RiscV(RiscVInlineAsmReg::parse(name)?) } @@ -610,7 +614,9 @@ impl InlineAsmRegClass { Self::X86(X86InlineAsmRegClass::parse(name)?) } InlineAsmArch::Arm => Self::Arm(ArmInlineAsmRegClass::parse(name)?), - InlineAsmArch::AArch64 => Self::AArch64(AArch64InlineAsmRegClass::parse(name)?), + InlineAsmArch::AArch64 | InlineAsmArch::Arm64EC => { + Self::AArch64(AArch64InlineAsmRegClass::parse(name)?) + } InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => { Self::RiscV(RiscVInlineAsmRegClass::parse(name)?) } @@ -783,7 +789,7 @@ pub fn allocatable_registers( arm::fill_reg_map(arch, reloc_model, target_features, target, &mut map); map } - InlineAsmArch::AArch64 => { + InlineAsmArch::AArch64 | InlineAsmArch::Arm64EC => { let mut map = aarch64::regclass_map(); aarch64::fill_reg_map(arch, reloc_model, target_features, target, &mut map); map @@ -909,6 +915,10 @@ impl InlineAsmClobberAbi { }), _ => Err(&["C", "system", "efiapi"]), }, + InlineAsmArch::Arm64EC => match name { + "C" | "system" => Ok(InlineAsmClobberAbi::AArch64NoX18), + _ => Err(&["C", "system"]), + }, InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => match name { "C" | "system" | "efiapi" => Ok(InlineAsmClobberAbi::RiscV), _ => Err(&["C", "system", "efiapi"]), diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index e94c7f3cc58..3a69b19ee60 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -448,6 +448,28 @@ impl LinkerFlavor { | LinkerFlavor::Ptx => false, } } + + /// For flavors with an `Lld` component, ensure it's enabled. Otherwise, returns the given + /// flavor unmodified. + pub fn with_lld_enabled(self) -> LinkerFlavor { + match self { + LinkerFlavor::Gnu(cc, Lld::No) => LinkerFlavor::Gnu(cc, Lld::Yes), + LinkerFlavor::Darwin(cc, Lld::No) => LinkerFlavor::Darwin(cc, Lld::Yes), + LinkerFlavor::Msvc(Lld::No) => LinkerFlavor::Msvc(Lld::Yes), + _ => self, + } + } + + /// For flavors with an `Lld` component, ensure it's disabled. Otherwise, returns the given + /// flavor unmodified. + pub fn with_lld_disabled(self) -> LinkerFlavor { + match self { + LinkerFlavor::Gnu(cc, Lld::Yes) => LinkerFlavor::Gnu(cc, Lld::No), + LinkerFlavor::Darwin(cc, Lld::Yes) => LinkerFlavor::Darwin(cc, Lld::No), + LinkerFlavor::Msvc(Lld::Yes) => LinkerFlavor::Msvc(Lld::No), + _ => self, + } + } } macro_rules! linker_flavor_cli_impls { @@ -698,6 +720,58 @@ impl ToJson for LinkSelfContainedComponents { } } +bitflags::bitflags! { + /// The `-Z linker-features` components that can individually be enabled or disabled. + /// + /// They are feature flags intended to be a more flexible mechanism than linker flavors, and + /// also to prevent a combinatorial explosion of flavors whenever a new linker feature is + /// required. These flags are "generic", in the sense that they can work on multiple targets on + /// the CLI. Otherwise, one would have to select different linkers flavors for each target. + /// + /// Here are some examples of the advantages they offer: + /// - default feature sets for principal flavors, or for specific targets. + /// - flavor-specific features: for example, clang offers automatic cross-linking with + /// `--target`, which gcc-style compilers don't support. The *flavor* is still a C/C++ + /// compiler, and we don't need to multiply the number of flavors for this use-case. Instead, + /// we can have a single `+target` feature. + /// - umbrella features: for example if clang accumulates more features in the future than just + /// the `+target` above. That could be modeled as `+clang`. + /// - niche features for resolving specific issues: for example, on Apple targets the linker + /// flag implementing the `as-needed` native link modifier (#99424) is only possible on + /// sufficiently recent linker versions. + /// - still allows for discovery and automation, for example via feature detection. This can be + /// useful in exotic environments/build systems. + #[derive(Clone, Copy, PartialEq, Eq, Default)] + pub struct LinkerFeatures: u8 { + /// Invoke the linker via a C/C++ compiler (e.g. on most unix targets). + const CC = 1 << 0; + /// Use the lld linker, either the system lld or the self-contained linker `rust-lld`. + const LLD = 1 << 1; + } +} +rustc_data_structures::external_bitflags_debug! { LinkerFeatures } + +impl LinkerFeatures { + /// Parses a single `-Z linker-features` well-known feature, not a set of flags. + pub fn from_str(s: &str) -> Option<LinkerFeatures> { + Some(match s { + "cc" => LinkerFeatures::CC, + "lld" => LinkerFeatures::LLD, + _ => return None, + }) + } + + /// Returns whether the `lld` linker feature is enabled. + pub fn is_lld_enabled(self) -> bool { + self.contains(LinkerFeatures::LLD) + } + + /// Returns whether the `cc` linker feature is enabled. + pub fn is_cc_enabled(self) -> bool { + self.contains(LinkerFeatures::CC) + } +} + #[derive(Clone, Copy, Debug, PartialEq, Hash, Encodable, Decodable, HashStable_Generic)] pub enum PanicStrategy { Unwind, diff --git a/compiler/rustc_target/src/spec/targets/i686_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/targets/i686_pc_windows_msvc.rs index becd2fd7afb..970b43ad109 100644 --- a/compiler/rustc_target/src/spec/targets/i686_pc_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/targets/i686_pc_windows_msvc.rs @@ -18,8 +18,6 @@ pub fn target() -> Target { "/SAFESEH", ], ); - // Workaround for #95429 - base.has_thread_local = false; Target { llvm_target: "i686-pc-windows-msvc".into(), diff --git a/compiler/rustc_target/src/spec/targets/i686_win7_windows_msvc.rs b/compiler/rustc_target/src/spec/targets/i686_win7_windows_msvc.rs index b68316f2830..ae1a44e44a8 100644 --- a/compiler/rustc_target/src/spec/targets/i686_win7_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/targets/i686_win7_windows_msvc.rs @@ -18,8 +18,6 @@ pub fn target() -> Target { "/SAFESEH", ], ); - // Workaround for #95429 - base.has_thread_local = false; Target { llvm_target: "i686-pc-windows-msvc".into(), diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index 8813955f2af..397e104512f 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -181,9 +181,9 @@ pub enum TyKind<I: Interner> { /// Looking at the following example, the witness for this coroutine /// may end up as something like `for<'a> [Vec<i32>, &'a Vec<i32>]`: /// - /// ```ignore UNSOLVED (ask @compiler-errors, should this error? can we just swap the yields?) + /// ``` /// #![feature(coroutines)] - /// |a| { + /// static |a| { /// let x = &vec![3]; /// yield a; /// yield x[0]; @@ -373,17 +373,8 @@ impl<I: Interner> DebugWithInfcx<I> for TyKind<I> { Array(t, c) => write!(f, "[{:?}; {:?}]", &this.wrap(t), &this.wrap(c)), Pat(t, p) => write!(f, "pattern_type!({:?} is {:?})", &this.wrap(t), &this.wrap(p)), Slice(t) => write!(f, "[{:?}]", &this.wrap(t)), - RawPtr(ty, mutbl) => { - match mutbl { - Mutability::Mut => write!(f, "*mut "), - Mutability::Not => write!(f, "*const "), - }?; - write!(f, "{:?}", &this.wrap(ty)) - } - Ref(r, t, m) => match m { - Mutability::Mut => write!(f, "&{:?} mut {:?}", &this.wrap(r), &this.wrap(t)), - Mutability::Not => write!(f, "&{:?} {:?}", &this.wrap(r), &this.wrap(t)), - }, + RawPtr(ty, mutbl) => write!(f, "*{} {:?}", mutbl.ptr_str(), this.wrap(ty)), + Ref(r, t, m) => write!(f, "&{:?} {}{:?}", this.wrap(r), m.prefix_str(), this.wrap(t)), FnDef(d, s) => f.debug_tuple("FnDef").field(d).field(&this.wrap(s)).finish(), FnPtr(s) => write!(f, "{:?}", &this.wrap(s)), Dynamic(p, r, repr) => match repr { diff --git a/config.example.toml b/config.example.toml index b8cdc2ec848..0d4b4e9e7e0 100644 --- a/config.example.toml +++ b/config.example.toml @@ -50,7 +50,7 @@ # # Note that many of the LLVM options are not currently supported for # downloading. Currently only the "assertions" option can be toggled. -#download-ci-llvm = if rust.channel == "dev" { "if-unchanged" } else { false } +#download-ci-llvm = if rust.channel == "dev" || rust.download-rustc != false { "if-unchanged" } else { false } # Indicates whether the LLVM build is a Release or Debug build #optimize = true @@ -302,7 +302,7 @@ # Set the bootstrap/download cache path. It is useful when building rust # repeatedly in a CI invironment. -# bootstrap-cache-path = /shared/cache +#bootstrap-cache-path = /path/to/shared/cache # Enable a build of the extended Rust tool set which is not only the compiler # but also tools such as Cargo. This will also produce "combined installers" diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index f638c5cf8c7..dec04d7e421 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -151,7 +151,6 @@ #![feature(slice_from_ptr_range)] #![feature(slice_index_methods)] #![feature(slice_ptr_get)] -#![feature(slice_ptr_len)] #![feature(slice_range)] #![feature(std_internals)] #![feature(str_internals)] diff --git a/library/alloc/src/raw_vec.rs b/library/alloc/src/raw_vec.rs index 0883080d735..1134c7f833e 100644 --- a/library/alloc/src/raw_vec.rs +++ b/library/alloc/src/raw_vec.rs @@ -259,6 +259,17 @@ impl<T, A: Allocator> RawVec<T, A> { Self { ptr: unsafe { Unique::new_unchecked(ptr) }, cap, alloc } } + /// A convenience method for hoisting the non-null precondition out of [`RawVec::from_raw_parts_in`]. + /// + /// # Safety + /// + /// See [`RawVec::from_raw_parts_in`]. + #[inline] + pub(crate) unsafe fn from_nonnull_in(ptr: NonNull<T>, capacity: usize, alloc: A) -> Self { + let cap = if T::IS_ZST { Cap::ZERO } else { unsafe { Cap(capacity) } }; + Self { ptr: Unique::from(ptr), cap, alloc } + } + /// Gets a raw pointer to the start of the allocation. Note that this is /// `Unique::dangling()` if `capacity == 0` or `T` is zero-sized. In the former case, you must /// be careful. diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 569db54b137..a320a244abd 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -884,7 +884,10 @@ impl<T, A: Allocator> Rc<T, A> { #[cfg(not(no_global_oom_handling))] #[unstable(feature = "allocator_api", issue = "32838")] #[inline] - pub fn pin_in(value: T, alloc: A) -> Pin<Self> { + pub fn pin_in(value: T, alloc: A) -> Pin<Self> + where + A: 'static, + { unsafe { Pin::new_unchecked(Rc::new_in(value, alloc)) } } diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 6ae52cc7827..297a273d274 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -807,7 +807,10 @@ impl<T, A: Allocator> Arc<T, A> { #[cfg(not(no_global_oom_handling))] #[unstable(feature = "allocator_api", issue = "32838")] #[inline] - pub fn pin_in(data: T, alloc: A) -> Pin<Arc<T, A>> { + pub fn pin_in(data: T, alloc: A) -> Pin<Arc<T, A>> + where + A: 'static, + { unsafe { Pin::new_unchecked(Arc::new_in(data, alloc)) } } @@ -815,7 +818,10 @@ impl<T, A: Allocator> Arc<T, A> { /// fails. #[inline] #[unstable(feature = "allocator_api", issue = "32838")] - pub fn try_pin_in(data: T, alloc: A) -> Result<Pin<Arc<T, A>>, AllocError> { + pub fn try_pin_in(data: T, alloc: A) -> Result<Pin<Arc<T, A>>, AllocError> + where + A: 'static, + { unsafe { Ok(Pin::new_unchecked(Arc::try_new_in(data, alloc)?)) } } diff --git a/library/alloc/src/vec/in_place_collect.rs b/library/alloc/src/vec/in_place_collect.rs index 4907a45e881..88aa1b1b0e0 100644 --- a/library/alloc/src/vec/in_place_collect.rs +++ b/library/alloc/src/vec/in_place_collect.rs @@ -161,7 +161,7 @@ use core::iter::{InPlaceIterable, SourceIter, TrustedRandomAccessNoCoerce}; use core::marker::PhantomData; use core::mem::{self, ManuallyDrop, SizedTypeProperties}; use core::num::NonZero; -use core::ptr::{self, NonNull}; +use core::ptr; use super::{InPlaceDrop, InPlaceDstDataSrcBufDrop, SpecFromIter, SpecFromIterNested, Vec}; @@ -254,28 +254,30 @@ where let (src_buf, src_ptr, src_cap, mut dst_buf, dst_end, dst_cap) = unsafe { let inner = iterator.as_inner().as_into_iter(); ( - inner.buf.as_ptr(), + inner.buf, inner.ptr, inner.cap, - inner.buf.as_ptr() as *mut T, + inner.buf.cast::<T>(), inner.end as *const T, inner.cap * mem::size_of::<I::Src>() / mem::size_of::<T>(), ) }; // SAFETY: `dst_buf` and `dst_end` are the start and end of the buffer. - let len = unsafe { SpecInPlaceCollect::collect_in_place(&mut iterator, dst_buf, dst_end) }; + let len = unsafe { + SpecInPlaceCollect::collect_in_place(&mut iterator, dst_buf.as_ptr() as *mut T, dst_end) + }; let src = unsafe { iterator.as_inner().as_into_iter() }; // check if SourceIter contract was upheld // caveat: if they weren't we might not even make it to this point - debug_assert_eq!(src_buf, src.buf.as_ptr()); + debug_assert_eq!(src_buf, src.buf); // check InPlaceIterable contract. This is only possible if the iterator advanced the // source pointer at all. If it uses unchecked access via TrustedRandomAccess // then the source pointer will stay in its initial position and we can't use it as reference if src.ptr != src_ptr { debug_assert!( - unsafe { dst_buf.add(len) as *const _ } <= src.ptr.as_ptr(), + unsafe { dst_buf.add(len).cast() } <= src.ptr, "InPlaceIterable contract violation, write pointer advanced beyond read pointer" ); } @@ -315,10 +317,9 @@ where let dst_size = mem::size_of::<T>().unchecked_mul(dst_cap); let new_layout = Layout::from_size_align_unchecked(dst_size, dst_align); - let result = - alloc.shrink(NonNull::new_unchecked(dst_buf as *mut u8), old_layout, new_layout); + let result = alloc.shrink(dst_buf.cast(), old_layout, new_layout); let Ok(reallocated) = result else { handle_alloc_error(new_layout) }; - dst_buf = reallocated.as_ptr() as *mut T; + dst_buf = reallocated.cast::<T>(); } } else { debug_assert_eq!(src_cap * mem::size_of::<I::Src>(), dst_cap * mem::size_of::<T>()); @@ -326,7 +327,7 @@ where mem::forget(dst_guard); - let vec = unsafe { Vec::from_raw_parts(dst_buf, len, dst_cap) }; + let vec = unsafe { Vec::from_nonnull(dst_buf, len, dst_cap) }; vec } diff --git a/library/alloc/src/vec/in_place_drop.rs b/library/alloc/src/vec/in_place_drop.rs index 40a540b57fc..4050c250130 100644 --- a/library/alloc/src/vec/in_place_drop.rs +++ b/library/alloc/src/vec/in_place_drop.rs @@ -1,4 +1,5 @@ use core::marker::PhantomData; +use core::ptr::NonNull; use core::ptr::{self, drop_in_place}; use core::slice::{self}; @@ -31,7 +32,7 @@ impl<T> Drop for InPlaceDrop<T> { // the source allocation - i.e. before the reallocation happened - to avoid leaking them // if some other destructor panics. pub(super) struct InPlaceDstDataSrcBufDrop<Src, Dest> { - pub(super) ptr: *mut Dest, + pub(super) ptr: NonNull<Dest>, pub(super) len: usize, pub(super) src_cap: usize, pub(super) src: PhantomData<Src>, @@ -42,8 +43,8 @@ impl<Src, Dest> Drop for InPlaceDstDataSrcBufDrop<Src, Dest> { fn drop(&mut self) { unsafe { let _drop_allocation = - RawVec::<Src>::from_raw_parts_in(self.ptr.cast::<Src>(), self.src_cap, Global); - drop_in_place(core::ptr::slice_from_raw_parts_mut::<Dest>(self.ptr, self.len)); + RawVec::<Src>::from_nonnull_in(self.ptr.cast::<Src>(), self.src_cap, Global); + drop_in_place(core::ptr::slice_from_raw_parts_mut::<Dest>(self.ptr.as_ptr(), self.len)); }; } } diff --git a/library/alloc/src/vec/into_iter.rs b/library/alloc/src/vec/into_iter.rs index dfd42ca0619..b0226c84833 100644 --- a/library/alloc/src/vec/into_iter.rs +++ b/library/alloc/src/vec/into_iter.rs @@ -433,7 +433,7 @@ unsafe impl<#[may_dangle] T, A: Allocator> Drop for IntoIter<T, A> { // `IntoIter::alloc` is not used anymore after this and will be dropped by RawVec let alloc = ManuallyDrop::take(&mut self.0.alloc); // RawVec handles deallocation - let _ = RawVec::from_raw_parts_in(self.0.buf.as_ptr(), self.0.cap, alloc); + let _ = RawVec::from_nonnull_in(self.0.buf, self.0.cap, alloc); } } } diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 7e3463bc082..465da39f184 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -603,6 +603,17 @@ impl<T> Vec<T> { pub unsafe fn from_raw_parts(ptr: *mut T, length: usize, capacity: usize) -> Self { unsafe { Self::from_raw_parts_in(ptr, length, capacity, Global) } } + + /// A convenience method for hoisting the non-null precondition out of [`Vec::from_raw_parts`]. + /// + /// # Safety + /// + /// See [`Vec::from_raw_parts`]. + #[inline] + #[cfg(not(no_global_oom_handling))] // required by tests/run-make/alloc-no-oom-handling + pub(crate) unsafe fn from_nonnull(ptr: NonNull<T>, length: usize, capacity: usize) -> Self { + unsafe { Self::from_nonnull_in(ptr, length, capacity, Global) } + } } impl<T, A: Allocator> Vec<T, A> { @@ -820,6 +831,22 @@ impl<T, A: Allocator> Vec<T, A> { unsafe { Vec { buf: RawVec::from_raw_parts_in(ptr, capacity, alloc), len: length } } } + /// A convenience method for hoisting the non-null precondition out of [`Vec::from_raw_parts_in`]. + /// + /// # Safety + /// + /// See [`Vec::from_raw_parts_in`]. + #[inline] + #[cfg(not(no_global_oom_handling))] // required by tests/run-make/alloc-no-oom-handling + pub(crate) unsafe fn from_nonnull_in( + ptr: NonNull<T>, + length: usize, + capacity: usize, + alloc: A, + ) -> Self { + unsafe { Vec { buf: RawVec::from_nonnull_in(ptr, capacity, alloc), len: length } } + } + /// Decomposes a `Vec<T>` into its raw components: `(pointer, length, capacity)`. /// /// Returns the raw pointer to the underlying data, the length of diff --git a/library/alloc/src/vec/spec_from_iter.rs b/library/alloc/src/vec/spec_from_iter.rs index 33dd4139bc0..6646ae7bccb 100644 --- a/library/alloc/src/vec/spec_from_iter.rs +++ b/library/alloc/src/vec/spec_from_iter.rs @@ -51,7 +51,7 @@ impl<T> SpecFromIter<T, IntoIter<T>> for Vec<T> { if has_advanced { ptr::copy(it.ptr.as_ptr(), it.buf.as_ptr(), it.len()); } - return Vec::from_raw_parts(it.buf.as_ptr(), it.len(), it.cap); + return Vec::from_nonnull(it.buf, it.len(), it.cap); } } diff --git a/library/backtrace b/library/backtrace -Subproject 6fa4b85b9962c3e1be8c2e5cc605cd078134152 +Subproject e15130618237eb3e2d4b622549f9647b4c1d9ca diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index 44ae72bcd26..d448c5338fc 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -227,13 +227,10 @@ mod impls { impl_clone! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 - f32 f64 + f16 f32 f64 f128 bool char } - #[cfg(not(bootstrap))] - impl_clone! { f16 f128 } - #[unstable(feature = "never_type", issue = "35121")] impl Clone for ! { #[inline] diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index 81bba927554..fa218600ed9 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -1497,12 +1497,9 @@ mod impls { } partial_eq_impl! { - bool char usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 + bool char usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f16 f32 f64 f128 } - #[cfg(not(bootstrap))] - partial_eq_impl! { f16 f128 } - macro_rules! eq_impl { ($($t:ty)*) => ($( #[stable(feature = "rust1", since = "1.0.0")] @@ -1553,10 +1550,7 @@ mod impls { } } - partial_ord_impl! { f32 f64 } - - #[cfg(not(bootstrap))] - partial_ord_impl! { f16 f128 } + partial_ord_impl! { f16 f32 f64 f128 } macro_rules! ord_impl { ($($t:ty)*) => ($( diff --git a/library/core/src/convert/num.rs b/library/core/src/convert/num.rs index 0167d04c413..935ead2699a 100644 --- a/library/core/src/convert/num.rs +++ b/library/core/src/convert/num.rs @@ -34,8 +34,10 @@ macro_rules! impl_float_to_int { } } +impl_float_to_int!(f16 => u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize); impl_float_to_int!(f32 => u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize); impl_float_to_int!(f64 => u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize); +impl_float_to_int!(f128 => u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize); // Conversion traits for primitive integer and float types // Conversions T -> T are covered by a blanket impl and therefore excluded @@ -163,7 +165,12 @@ impl_from!(u16 => f64, #[stable(feature = "lossless_float_conv", since = "1.6.0" impl_from!(u32 => f64, #[stable(feature = "lossless_float_conv", since = "1.6.0")]); // float -> float +// FIXME(f16_f128): adding additional `From` impls for existing types breaks inference. See +// <https://github.com/rust-lang/rust/issues/123824> +impl_from!(f16 => f128, #[stable(feature = "lossless_float_conv", since = "1.6.0")]); impl_from!(f32 => f64, #[stable(feature = "lossless_float_conv", since = "1.6.0")]); +impl_from!(f32 => f128, #[stable(feature = "lossless_float_conv", since = "1.6.0")]); +impl_from!(f64 => f128, #[stable(feature = "lossless_float_conv", since = "1.6.0")]); macro_rules! impl_float_from_bool { ($float:ty) => { diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index dbdbaccb535..f4f33f8584b 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -517,8 +517,6 @@ impl CStr { /// # Examples /// /// ``` - /// #![feature(cstr_count_bytes)] - /// /// use std::ffi::CStr; /// /// let cstr = CStr::from_bytes_with_nul(b"foo\0").unwrap(); @@ -530,7 +528,7 @@ impl CStr { #[inline] #[must_use] #[doc(alias("len", "strlen"))] - #[unstable(feature = "cstr_count_bytes", issue = "114441")] + #[stable(feature = "cstr_count_bytes", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_unstable(feature = "const_cstr_from_ptr", issue = "113219")] pub const fn count_bytes(&self) -> usize { self.inner.len() - 1 diff --git a/library/core/src/fmt/float.rs b/library/core/src/fmt/float.rs index 3bbf5d8770b..7f23d3c0956 100644 --- a/library/core/src/fmt/float.rs +++ b/library/core/src/fmt/float.rs @@ -228,3 +228,19 @@ macro_rules! floating { floating! { f32 } floating! { f64 } + +#[stable(feature = "rust1", since = "1.0.0")] +impl Debug for f16 { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "{:#06x}", self.to_bits()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Debug for f128 { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "{:#034x}", self.to_bits()) + } +} diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 287f6c23c89..ce0643a3f5e 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -860,10 +860,10 @@ pub trait Binary { /// Basic usage with `i32`: /// /// ``` -/// let x = 42; // 42 is '2a' in hex +/// let y = 42; // 42 is '2a' in hex /// -/// assert_eq!(format!("{x:x}"), "2a"); -/// assert_eq!(format!("{x:#x}"), "0x2a"); +/// assert_eq!(format!("{y:x}"), "2a"); +/// assert_eq!(format!("{y:#x}"), "0x2a"); /// /// assert_eq!(format!("{:x}", -16), "fffffff0"); /// ``` @@ -915,10 +915,10 @@ pub trait LowerHex { /// Basic usage with `i32`: /// /// ``` -/// let x = 42; // 42 is '2A' in hex +/// let y = 42; // 42 is '2A' in hex /// -/// assert_eq!(format!("{x:X}"), "2A"); -/// assert_eq!(format!("{x:#X}"), "0x2A"); +/// assert_eq!(format!("{y:X}"), "2A"); +/// assert_eq!(format!("{y:#X}"), "0x2A"); /// /// assert_eq!(format!("{:X}", -16), "FFFFFFF0"); /// ``` @@ -1150,7 +1150,12 @@ pub fn write(output: &mut dyn Write, args: Arguments<'_>) -> Result { if !piece.is_empty() { formatter.buf.write_str(*piece)?; } - arg.fmt(&mut formatter)?; + + // SAFETY: There are no formatting parameters and hence no + // count arguments. + unsafe { + arg.fmt(&mut formatter)?; + } idx += 1; } } @@ -1198,7 +1203,8 @@ unsafe fn run(fmt: &mut Formatter<'_>, arg: &rt::Placeholder, args: &[rt::Argume let value = unsafe { args.get_unchecked(arg.position) }; // Then actually do some printing - value.fmt(fmt) + // SAFETY: this is a placeholder argument. + unsafe { value.fmt(fmt) } } unsafe fn getcount(args: &[rt::Argument<'_>], cnt: &rt::Count) -> Option<usize> { diff --git a/library/core/src/fmt/nofloat.rs b/library/core/src/fmt/nofloat.rs index cfb94cd9de5..a36e7efcd95 100644 --- a/library/core/src/fmt/nofloat.rs +++ b/library/core/src/fmt/nofloat.rs @@ -11,5 +11,7 @@ macro_rules! floating { }; } +floating! { f16 } floating! { f32 } floating! { f64 } +floating! { f128 } diff --git a/library/core/src/fmt/rt.rs b/library/core/src/fmt/rt.rs index 5bf221b429f..5e4dac8f49b 100644 --- a/library/core/src/fmt/rt.rs +++ b/library/core/src/fmt/rt.rs @@ -4,6 +4,7 @@ //! These are the lang items used by format_args!(). use super::*; +use crate::hint::unreachable_unchecked; #[lang = "format_placeholder"] #[derive(Copy, Clone)] @@ -63,18 +64,26 @@ pub(super) enum Flag { DebugUpperHex, } -/// This struct represents the generic "argument" which is taken by format_args!(). -/// It contains a function to format the given value. At compile time it is ensured that the -/// function and the value have the correct types, and then this struct is used to canonicalize -/// arguments to one type. +#[derive(Copy, Clone)] +enum ArgumentType<'a> { + Placeholder { value: &'a Opaque, formatter: fn(&Opaque, &mut Formatter<'_>) -> Result }, + Count(usize), +} + +/// This struct represents a generic "argument" which is taken by format_args!(). /// -/// Argument is essentially an optimized partially applied formatting function, -/// equivalent to `exists T.(&T, fn(&T, &mut Formatter<'_>) -> Result`. +/// This can be either a placeholder argument or a count argument. +/// * A placeholder argument contains a function to format the given value. At +/// compile time it is ensured that the function and the value have the correct +/// types, and then this struct is used to canonicalize arguments to one type. +/// Placeholder arguments are essentially an optimized partially applied formatting +/// function, equivalent to `exists T.(&T, fn(&T, &mut Formatter<'_>) -> Result`. +/// * A count argument contains a count for dynamic formatting parameters like +/// precision and width. #[lang = "format_argument"] #[derive(Copy, Clone)] pub struct Argument<'a> { - value: &'a Opaque, - formatter: fn(&Opaque, &mut Formatter<'_>) -> Result, + ty: ArgumentType<'a>, } #[rustc_diagnostic_item = "ArgumentMethods"] @@ -89,7 +98,14 @@ impl<'a> Argument<'a> { // `mem::transmute(f)` is safe since `fn(&T, &mut Formatter<'_>) -> Result` // and `fn(&Opaque, &mut Formatter<'_>) -> Result` have the same ABI // (as long as `T` is `Sized`) - unsafe { Argument { formatter: mem::transmute(f), value: mem::transmute(x) } } + unsafe { + Argument { + ty: ArgumentType::Placeholder { + formatter: mem::transmute(f), + value: mem::transmute(x), + }, + } + } } #[inline(always)] @@ -130,30 +146,33 @@ impl<'a> Argument<'a> { } #[inline(always)] pub fn from_usize(x: &usize) -> Argument<'_> { - Self::new(x, USIZE_MARKER) + Argument { ty: ArgumentType::Count(*x) } } + /// Format this placeholder argument. + /// + /// # Safety + /// + /// This argument must actually be a placeholer argument. + /// // FIXME: Transmuting formatter in new and indirectly branching to/calling // it here is an explicit CFI violation. #[allow(inline_no_sanitize)] #[no_sanitize(cfi, kcfi)] #[inline(always)] - pub(super) fn fmt(&self, f: &mut Formatter<'_>) -> Result { - (self.formatter)(self.value, f) + pub(super) unsafe fn fmt(&self, f: &mut Formatter<'_>) -> Result { + match self.ty { + ArgumentType::Placeholder { formatter, value } => formatter(value, f), + // SAFETY: the caller promised this. + ArgumentType::Count(_) => unsafe { unreachable_unchecked() }, + } } #[inline(always)] pub(super) fn as_usize(&self) -> Option<usize> { - // We are type punning a bit here: USIZE_MARKER only takes an &usize but - // formatter takes an &Opaque. Rust understandably doesn't think we should compare - // the function pointers if they don't have the same signature, so we cast to - // usizes to tell it that we just want to compare addresses. - if self.formatter as usize == USIZE_MARKER as usize { - // SAFETY: The `formatter` field is only set to USIZE_MARKER if - // the value is a usize, so this is safe - Some(unsafe { *(self.value as *const _ as *const usize) }) - } else { - None + match self.ty { + ArgumentType::Count(count) => Some(count), + ArgumentType::Placeholder { .. } => None, } } @@ -193,24 +212,3 @@ impl UnsafeArg { extern "C" { type Opaque; } - -// This guarantees a single stable value for the function pointer associated with -// indices/counts in the formatting infrastructure. -// -// Note that a function defined as such would not be correct as functions are -// always tagged unnamed_addr with the current lowering to LLVM IR, so their -// address is not considered important to LLVM and as such the as_usize cast -// could have been miscompiled. In practice, we never call as_usize on non-usize -// containing data (as a matter of static generation of the formatting -// arguments), so this is merely an additional check. -// -// We primarily want to ensure that the function pointer at `USIZE_MARKER` has -// an address corresponding *only* to functions that also take `&usize` as their -// first argument. The read_volatile here ensures that we can safely ready out a -// usize from the passed reference and that this address does not point at a -// non-usize taking function. -static USIZE_MARKER: fn(&usize, &mut Formatter<'_>) -> Result = |ptr, _| { - // SAFETY: ptr is a reference - let _v: usize = unsafe { crate::ptr::read_volatile(ptr) }; - loop {} -}; diff --git a/library/core/src/io/borrowed_buf.rs b/library/core/src/io/borrowed_buf.rs index 778d38b1537..81371708b51 100644 --- a/library/core/src/io/borrowed_buf.rs +++ b/library/core/src/io/borrowed_buf.rs @@ -249,9 +249,10 @@ impl<'a> BorrowedCursor<'a> { /// Panics if there are less than `n` bytes initialized. #[inline] pub fn advance(&mut self, n: usize) -> &mut Self { - assert!(self.buf.init >= self.buf.filled + n); + let filled = self.buf.filled.strict_add(n); + assert!(filled <= self.buf.init); - self.buf.filled += n; + self.buf.filled = filled; self } diff --git a/library/core/src/iter/traits/collect.rs b/library/core/src/iter/traits/collect.rs index d89801bce2b..2ebbe2bf274 100644 --- a/library/core/src/iter/traits/collect.rs +++ b/library/core/src/iter/traits/collect.rs @@ -150,6 +150,39 @@ pub trait FromIterator<A>: Sized { fn from_iter<T: IntoIterator<Item = A>>(iter: T) -> Self; } +/// This implementation turns an iterator of tuples into a tuple of types which implement +/// [`Default`] and [`Extend`]. +/// +/// This is similar to [`Iterator::unzip`], but is also composable with other [`FromIterator`] +/// implementations: +/// +/// ```rust +/// # fn main() -> Result<(), core::num::ParseIntError> { +/// let string = "1,2,123,4"; +/// +/// let (numbers, lengths): (Vec<_>, Vec<_>) = string +/// .split(',') +/// .map(|s| s.parse().map(|n: u32| (n, s.len()))) +/// .collect::<Result<_, _>>()?; +/// +/// assert_eq!(numbers, [1, 2, 123, 4]); +/// assert_eq!(lengths, [1, 1, 3, 1]); +/// # Ok(()) } +/// ``` +#[stable(feature = "from_iterator_for_tuple", since = "CURRENT_RUSTC_VERSION")] +impl<A, B, AE, BE> FromIterator<(AE, BE)> for (A, B) +where + A: Default + Extend<AE>, + B: Default + Extend<BE>, +{ + fn from_iter<I: IntoIterator<Item = (AE, BE)>>(iter: I) -> Self { + let mut res = <(A, B)>::default(); + res.extend(iter); + + res + } +} + /// Conversion into an [`Iterator`]. /// /// By implementing `IntoIterator` for a type, you define how it will be diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 0ba3a557a03..d2618c8bb2b 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -111,6 +111,7 @@ // tidy-alphabetical-start #![cfg_attr(bootstrap, feature(associated_type_bounds))] #![feature(array_ptr_get)] +#![feature(asm_experimental_arch)] #![feature(char_indices_offset)] #![feature(const_align_of_val)] #![feature(const_align_of_val_raw)] @@ -158,7 +159,6 @@ #![feature(const_slice_from_raw_parts_mut)] #![feature(const_slice_from_ref)] #![feature(const_slice_index)] -#![feature(const_slice_ptr_len)] #![feature(const_slice_split_at_mut)] #![feature(const_str_from_utf8_unchecked_mut)] #![feature(const_strict_overflow_ops)] @@ -200,8 +200,6 @@ // // Language features: // tidy-alphabetical-start -#![cfg_attr(not(bootstrap), feature(f128))] -#![cfg_attr(not(bootstrap), feature(f16))] #![feature(abi_unadjusted)] #![feature(adt_const_params)] #![feature(allow_internal_unsafe)] @@ -226,6 +224,8 @@ #![feature(doc_notable_trait)] #![feature(effects)] #![feature(extern_types)] +#![feature(f128)] +#![feature(f16)] #![feature(freeze_impls)] #![feature(fundamental)] #![feature(generic_arg_infer)] @@ -347,6 +347,10 @@ pub mod u8; #[path = "num/shells/usize.rs"] pub mod usize; +#[path = "num/f128.rs"] +pub mod f128; +#[path = "num/f16.rs"] +pub mod f16; #[path = "num/f32.rs"] pub mod f32; #[path = "num/f64.rs"] diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index fb97b3bfa09..1d073a6d649 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -422,17 +422,11 @@ marker_impls! { Copy for usize, u8, u16, u32, u64, u128, isize, i8, i16, i32, i64, i128, - f32, f64, + f16, f32, f64, f128, bool, char, {T: ?Sized} *const T, {T: ?Sized} *mut T, -} -#[cfg(not(bootstrap))] -marker_impls! { - #[stable(feature = "rust1", since = "1.0.0")] - Copy for - f16, f128, } #[unstable(feature = "never_type", issue = "35121")] diff --git a/library/core/src/net/socket_addr.rs b/library/core/src/net/socket_addr.rs index 55116285842..c24d8f55195 100644 --- a/library/core/src/net/socket_addr.rs +++ b/library/core/src/net/socket_addr.rs @@ -591,7 +591,7 @@ impl fmt::Display for SocketAddrV4 { if f.precision().is_none() && f.width().is_none() { write!(f, "{}:{}", self.ip(), self.port()) } else { - const LONGEST_IPV4_SOCKET_ADDR: &str = "255.255.255.255:65536"; + const LONGEST_IPV4_SOCKET_ADDR: &str = "255.255.255.255:65535"; let mut buf = DisplayBuffer::<{ LONGEST_IPV4_SOCKET_ADDR.len() }>::new(); // Buffer is long enough for the longest possible IPv4 socket address, so this should never fail. @@ -621,7 +621,7 @@ impl fmt::Display for SocketAddrV6 { } } else { const LONGEST_IPV6_SOCKET_ADDR: &str = - "[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff%4294967296]:65536"; + "[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff%4294967295]:65535"; let mut buf = DisplayBuffer::<{ LONGEST_IPV6_SOCKET_ADDR.len() }>::new(); match self.scope_id() { diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs new file mode 100644 index 00000000000..8a94964c8c5 --- /dev/null +++ b/library/core/src/num/f128.rs @@ -0,0 +1,120 @@ +//! Constants for the `f128` quadruple-precision floating point type. +//! +//! *[See also the `f128` primitive type][f128].* +//! +//! Mathematically significant numbers are provided in the `consts` sub-module. +//! +//! For the constants defined directly in this module +//! (as distinct from those defined in the `consts` sub-module), +//! new code should instead use the associated constants +//! defined directly on the `f128` type. + +#![unstable(feature = "f128", issue = "116909")] + +use crate::mem; + +/// Basic mathematical constants. +#[unstable(feature = "f128", issue = "116909")] +pub mod consts {} + +#[cfg(not(test))] +impl f128 { + // FIXME(f16_f128): almost everything in this `impl` is missing examples and a const + // implementation. Add these once we can run code on all platforms and have f16/f128 in CTFE. + + /// Returns `true` if this value is NaN. + #[inline] + #[must_use] + #[unstable(feature = "f128", issue = "116909")] + #[allow(clippy::eq_op)] // > if you intended to check if the operand is NaN, use `.is_nan()` instead :) + pub const fn is_nan(self) -> bool { + self != self + } + + /// Returns `true` if `self` has a positive sign, including `+0.0`, NaNs with + /// positive sign bit and positive infinity. Note that IEEE 754 doesn't assign any + /// meaning to the sign bit in case of a NaN, and as Rust doesn't guarantee that + /// the bit pattern of NaNs are conserved over arithmetic operations, the result of + /// `is_sign_positive` on a NaN might produce an unexpected result in some cases. + /// See [explanation of NaN as a special value](f32) for more info. + #[inline] + #[must_use] + #[unstable(feature = "f128", issue = "116909")] + pub fn is_sign_positive(self) -> bool { + !self.is_sign_negative() + } + + /// Returns `true` if `self` has a negative sign, including `-0.0`, NaNs with + /// negative sign bit and negative infinity. Note that IEEE 754 doesn't assign any + /// meaning to the sign bit in case of a NaN, and as Rust doesn't guarantee that + /// the bit pattern of NaNs are conserved over arithmetic operations, the result of + /// `is_sign_negative` on a NaN might produce an unexpected result in some cases. + /// See [explanation of NaN as a special value](f32) for more info. + #[inline] + #[must_use] + #[unstable(feature = "f128", issue = "116909")] + pub fn is_sign_negative(self) -> bool { + // IEEE754 says: isSignMinus(x) is true if and only if x has negative sign. isSignMinus + // applies to zeros and NaNs as well. + // SAFETY: This is just transmuting to get the sign bit, it's fine. + (self.to_bits() & (1 << 127)) != 0 + } + + /// Raw transmutation to `u128`. + /// + /// This is currently identical to `transmute::<f128, u128>(self)` on all platforms. + /// + /// See [`from_bits`](#method.from_bits) for some discussion of the + /// portability of this operation (there are almost no issues). + /// + /// Note that this function is distinct from `as` casting, which attempts to + /// preserve the *numeric* value, and not the bitwise value. + #[inline] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn to_bits(self) -> u128 { + // SAFETY: `u128` is a plain old datatype so we can always... uh... + // ...look, just pretend you forgot what you just read. + // Stability concerns. + unsafe { mem::transmute(self) } + } + + /// Raw transmutation from `u128`. + /// + /// This is currently identical to `transmute::<u128, f128>(v)` on all platforms. + /// It turns out this is incredibly portable, for two reasons: + /// + /// * Floats and Ints have the same endianness on all supported platforms. + /// * IEEE 754 very precisely specifies the bit layout of floats. + /// + /// However there is one caveat: prior to the 2008 version of IEEE 754, how + /// to interpret the NaN signaling bit wasn't actually specified. Most platforms + /// (notably x86 and ARM) picked the interpretation that was ultimately + /// standardized in 2008, but some didn't (notably MIPS). As a result, all + /// signaling NaNs on MIPS are quiet NaNs on x86, and vice-versa. + /// + /// Rather than trying to preserve signaling-ness cross-platform, this + /// implementation favors preserving the exact bits. This means that + /// any payloads encoded in NaNs will be preserved even if the result of + /// this method is sent over the network from an x86 machine to a MIPS one. + /// + /// If the results of this method are only manipulated by the same + /// architecture that produced them, then there is no portability concern. + /// + /// If the input isn't NaN, then there is no portability concern. + /// + /// If you don't care about signalingness (very likely), then there is no + /// portability concern. + /// + /// Note that this function is distinct from `as` casting, which attempts to + /// preserve the *numeric* value, and not the bitwise value. + #[inline] + #[must_use] + #[unstable(feature = "f128", issue = "116909")] + pub fn from_bits(v: u128) -> Self { + // SAFETY: `u128 is a plain old datatype so we can always... uh... + // ...look, just pretend you forgot what you just read. + // Stability concerns. + unsafe { mem::transmute(v) } + } +} diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs new file mode 100644 index 00000000000..039f5188ba4 --- /dev/null +++ b/library/core/src/num/f16.rs @@ -0,0 +1,120 @@ +//! Constants for the `f16` half-precision floating point type. +//! +//! *[See also the `f16` primitive type][f16].* +//! +//! Mathematically significant numbers are provided in the `consts` sub-module. +//! +//! For the constants defined directly in this module +//! (as distinct from those defined in the `consts` sub-module), +//! new code should instead use the associated constants +//! defined directly on the `f16` type. + +#![unstable(feature = "f16", issue = "116909")] + +use crate::mem; + +/// Basic mathematical constants. +#[unstable(feature = "f16", issue = "116909")] +pub mod consts {} + +#[cfg(not(test))] +impl f16 { + // FIXME(f16_f128): almost everything in this `impl` is missing examples and a const + // implementation. Add these once we can run code on all platforms and have f16/f128 in CTFE. + + /// Returns `true` if this value is NaN. + #[inline] + #[must_use] + #[unstable(feature = "f16", issue = "116909")] + #[allow(clippy::eq_op)] // > if you intended to check if the operand is NaN, use `.is_nan()` instead :) + pub const fn is_nan(self) -> bool { + self != self + } + + /// Returns `true` if `self` has a positive sign, including `+0.0`, NaNs with + /// positive sign bit and positive infinity. Note that IEEE 754 doesn't assign any + /// meaning to the sign bit in case of a NaN, and as Rust doesn't guarantee that + /// the bit pattern of NaNs are conserved over arithmetic operations, the result of + /// `is_sign_positive` on a NaN might produce an unexpected result in some cases. + /// See [explanation of NaN as a special value](f32) for more info. + #[inline] + #[must_use] + #[unstable(feature = "f128", issue = "116909")] + pub fn is_sign_positive(self) -> bool { + !self.is_sign_negative() + } + + /// Returns `true` if `self` has a negative sign, including `-0.0`, NaNs with + /// negative sign bit and negative infinity. Note that IEEE 754 doesn't assign any + /// meaning to the sign bit in case of a NaN, and as Rust doesn't guarantee that + /// the bit pattern of NaNs are conserved over arithmetic operations, the result of + /// `is_sign_negative` on a NaN might produce an unexpected result in some cases. + /// See [explanation of NaN as a special value](f32) for more info. + #[inline] + #[must_use] + #[unstable(feature = "f128", issue = "116909")] + pub fn is_sign_negative(self) -> bool { + // IEEE754 says: isSignMinus(x) is true if and only if x has negative sign. isSignMinus + // applies to zeros and NaNs as well. + // SAFETY: This is just transmuting to get the sign bit, it's fine. + (self.to_bits() & (1 << 15)) != 0 + } + + /// Raw transmutation to `u16`. + /// + /// This is currently identical to `transmute::<f16, u16>(self)` on all platforms. + /// + /// See [`from_bits`](#method.from_bits) for some discussion of the + /// portability of this operation (there are almost no issues). + /// + /// Note that this function is distinct from `as` casting, which attempts to + /// preserve the *numeric* value, and not the bitwise value. + #[inline] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn to_bits(self) -> u16 { + // SAFETY: `u16` is a plain old datatype so we can always... uh... + // ...look, just pretend you forgot what you just read. + // Stability concerns. + unsafe { mem::transmute(self) } + } + + /// Raw transmutation from `u16`. + /// + /// This is currently identical to `transmute::<u16, f16>(v)` on all platforms. + /// It turns out this is incredibly portable, for two reasons: + /// + /// * Floats and Ints have the same endianness on all supported platforms. + /// * IEEE 754 very precisely specifies the bit layout of floats. + /// + /// However there is one caveat: prior to the 2008 version of IEEE 754, how + /// to interpret the NaN signaling bit wasn't actually specified. Most platforms + /// (notably x86 and ARM) picked the interpretation that was ultimately + /// standardized in 2008, but some didn't (notably MIPS). As a result, all + /// signaling NaNs on MIPS are quiet NaNs on x86, and vice-versa. + /// + /// Rather than trying to preserve signaling-ness cross-platform, this + /// implementation favors preserving the exact bits. This means that + /// any payloads encoded in NaNs will be preserved even if the result of + /// this method is sent over the network from an x86 machine to a MIPS one. + /// + /// If the results of this method are only manipulated by the same + /// architecture that produced them, then there is no portability concern. + /// + /// If the input isn't NaN, then there is no portability concern. + /// + /// If you don't care about signalingness (very likely), then there is no + /// portability concern. + /// + /// Note that this function is distinct from `as` casting, which attempts to + /// preserve the *numeric* value, and not the bitwise value. + #[inline] + #[must_use] + #[unstable(feature = "f16", issue = "116909")] + pub fn from_bits(v: u16) -> Self { + // SAFETY: `u16` is a plain old datatype so we can always... uh... + // ...look, just pretend you forgot what you just read. + // Stability concerns. + unsafe { mem::transmute(v) } + } +} diff --git a/library/core/src/ops/arith.rs b/library/core/src/ops/arith.rs index fd50f804748..5e77788d8ea 100644 --- a/library/core/src/ops/arith.rs +++ b/library/core/src/ops/arith.rs @@ -109,7 +109,7 @@ macro_rules! add_impl { )*) } -add_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 } +add_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f16 f32 f64 f128 } /// The subtraction operator `-`. /// @@ -218,7 +218,7 @@ macro_rules! sub_impl { )*) } -sub_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 } +sub_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f16 f32 f64 f128 } /// The multiplication operator `*`. /// @@ -348,7 +348,7 @@ macro_rules! mul_impl { )*) } -mul_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 } +mul_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f16 f32 f64 f128 } /// The division operator `/`. /// @@ -506,7 +506,7 @@ macro_rules! div_impl_float { )*) } -div_impl_float! { f32 f64 } +div_impl_float! { f16 f32 f64 f128 } /// The remainder operator `%`. /// @@ -623,7 +623,7 @@ macro_rules! rem_impl_float { )*) } -rem_impl_float! { f32 f64 } +rem_impl_float! { f16 f32 f64 f128 } /// The unary negation operator `-`. /// @@ -698,7 +698,7 @@ macro_rules! neg_impl { )*) } -neg_impl! { isize i8 i16 i32 i64 i128 f32 f64 } +neg_impl! { isize i8 i16 i32 i64 i128 f16 f32 f64 f128 } /// The addition assignment operator `+=`. /// @@ -765,7 +765,7 @@ macro_rules! add_assign_impl { )+) } -add_assign_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 } +add_assign_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f16 f32 f64 f128 } /// The subtraction assignment operator `-=`. /// @@ -832,7 +832,7 @@ macro_rules! sub_assign_impl { )+) } -sub_assign_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 } +sub_assign_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f16 f32 f64 f128 } /// The multiplication assignment operator `*=`. /// @@ -890,7 +890,7 @@ macro_rules! mul_assign_impl { )+) } -mul_assign_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 } +mul_assign_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f16 f32 f64 f128 } /// The division assignment operator `/=`. /// @@ -947,7 +947,7 @@ macro_rules! div_assign_impl { )+) } -div_assign_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 } +div_assign_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f16 f32 f64 f128 } /// The remainder assignment operator `%=`. /// @@ -1008,4 +1008,4 @@ macro_rules! rem_assign_impl { )+) } -rem_assign_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 } +rem_assign_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f16 f32 f64 f128 } diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index e843a5d5790..efd525aeb3b 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -379,11 +379,11 @@ //! //! Exposing access to the inner field which you want to remain pinned must then be carefully //! considered as well! Remember, exposing a method that gives access to a -//! <code>[Pin]<[&mut] InnerT>></code> where `InnerT: [Unpin]` would allow safe code to trivially -//! move the inner value out of that pinning pointer, which is precisely what you're seeking to -//! prevent! Exposing a field of a pinned value through a pinning pointer is called "projecting" -//! a pin, and the more general case of deciding in which cases a pin should be able to be -//! projected or not is called "structural pinning." We will go into more detail about this +//! <code>[Pin]<[&mut] InnerT>></code> where <code>InnerT: [Unpin]</code> would allow safe code to +//! trivially move the inner value out of that pinning pointer, which is precisely what you're +//! seeking to prevent! Exposing a field of a pinned value through a pinning pointer is called +//! "projecting" a pin, and the more general case of deciding in which cases a pin should be able +//! to be projected or not is called "structural pinning." We will go into more detail about this //! [below][structural-pinning]. //! //! # Examples of address-sensitive types @@ -1198,7 +1198,7 @@ impl<Ptr: Deref<Target: Unpin>> Pin<Ptr> { /// Unwraps this `Pin<Ptr>`, returning the underlying pointer. /// /// Doing this operation safely requires that the data pointed at by this pinning pointer - /// implemts [`Unpin`] so that we can ignore the pinning invariants when unwrapping it. + /// implements [`Unpin`] so that we can ignore the pinning invariants when unwrapping it. /// /// # Examples /// diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index 15da4171bda..e8e23f2a7ec 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -1074,7 +1074,22 @@ mod prim_tuple {} #[doc(hidden)] impl<T> (T,) {} +#[rustc_doc_primitive = "f16"] +#[doc(alias = "half")] +/// A 16-bit floating point type (specifically, the "binary16" type defined in IEEE 754-2008). +/// +/// This type is very similar to [`prim@f32`] but has decreased precision because it uses half as many +/// bits. Please see [the documentation for [`prim@f32`] or [Wikipedia on +/// half-precision values][wikipedia] for more information. +/// +/// *[See also the `std::f16::consts` module](crate::f16::consts).* +/// +/// [wikipedia]: https://en.wikipedia.org/wiki/Half-precision_floating-point_format +#[unstable(feature = "f16", issue = "116909")] +mod prim_f16 {} + #[rustc_doc_primitive = "f32"] +#[doc(alias = "single")] /// A 32-bit floating point type (specifically, the "binary32" type defined in IEEE 754-2008). /// /// This type can represent a wide range of decimal numbers, like `3.5`, `27`, @@ -1143,6 +1158,7 @@ impl<T> (T,) {} mod prim_f32 {} #[rustc_doc_primitive = "f64"] +#[doc(alias = "double")] /// A 64-bit floating point type (specifically, the "binary64" type defined in IEEE 754-2008). /// /// This type is very similar to [`f32`], but has increased @@ -1157,6 +1173,20 @@ mod prim_f32 {} #[stable(feature = "rust1", since = "1.0.0")] mod prim_f64 {} +#[rustc_doc_primitive = "f128"] +#[doc(alias = "quad")] +/// A 128-bit floating point type (specifically, the "binary128" type defined in IEEE 754-2008). +/// +/// This type is very similar to [`prim@f32`] and [`prim@f64`], but has increased precision by using twice +/// as many bits as `f64`. Please see [the documentation for [`prim@f32`] or [Wikipedia on +/// quad-precision values][wikipedia] for more information. +/// +/// *[See also the `std::f128::consts` module](crate::f128::consts).* +/// +/// [wikipedia]: https://en.wikipedia.org/wiki/Quadruple-precision_floating-point_format +#[unstable(feature = "f128", issue = "116909")] +mod prim_f128 {} + #[rustc_doc_primitive = "i8"] // /// The 8-bit signed integer type. diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 01db050e666..9737fb8816e 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -1647,16 +1647,15 @@ impl<T> *const [T] { /// # Examples /// /// ```rust - /// #![feature(slice_ptr_len)] - /// /// use std::ptr; /// /// let slice: *const [i8] = ptr::slice_from_raw_parts(ptr::null(), 3); /// assert_eq!(slice.len(), 3); /// ``` #[inline] - #[unstable(feature = "slice_ptr_len", issue = "71146")] - #[rustc_const_unstable(feature = "const_slice_ptr_len", issue = "71146")] + #[stable(feature = "slice_ptr_len", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_slice_ptr_len", since = "CURRENT_RUSTC_VERSION")] + #[rustc_allow_const_fn_unstable(ptr_metadata)] pub const fn len(self) -> usize { metadata(self) } @@ -1666,15 +1665,14 @@ impl<T> *const [T] { /// # Examples /// /// ``` - /// #![feature(slice_ptr_len)] /// use std::ptr; /// /// let slice: *const [i8] = ptr::slice_from_raw_parts(ptr::null(), 3); /// assert!(!slice.is_empty()); /// ``` #[inline(always)] - #[unstable(feature = "slice_ptr_len", issue = "71146")] - #[rustc_const_unstable(feature = "const_slice_ptr_len", issue = "71146")] + #[stable(feature = "slice_ptr_len", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_slice_ptr_len", since = "CURRENT_RUSTC_VERSION")] pub const fn is_empty(self) -> bool { self.len() == 0 } @@ -1804,7 +1802,7 @@ impl<T, const N: usize> *const [T; N] { /// # Examples /// /// ``` - /// #![feature(array_ptr_get, slice_ptr_len)] + /// #![feature(array_ptr_get)] /// /// let arr: *const [i32; 3] = &[1, 2, 4] as *const [i32; 3]; /// let slice: *const [i32] = arr.as_slice(); diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 41e5ba67458..08f03af355d 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -1909,15 +1909,15 @@ impl<T> *mut [T] { /// # Examples /// /// ```rust - /// #![feature(slice_ptr_len)] /// use std::ptr; /// /// let slice: *mut [i8] = ptr::slice_from_raw_parts_mut(ptr::null_mut(), 3); /// assert_eq!(slice.len(), 3); /// ``` #[inline(always)] - #[unstable(feature = "slice_ptr_len", issue = "71146")] - #[rustc_const_unstable(feature = "const_slice_ptr_len", issue = "71146")] + #[stable(feature = "slice_ptr_len", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_slice_ptr_len", since = "CURRENT_RUSTC_VERSION")] + #[rustc_allow_const_fn_unstable(ptr_metadata)] pub const fn len(self) -> usize { metadata(self) } @@ -1927,15 +1927,14 @@ impl<T> *mut [T] { /// # Examples /// /// ``` - /// #![feature(slice_ptr_len)] /// use std::ptr; /// /// let slice: *mut [i8] = ptr::slice_from_raw_parts_mut(ptr::null_mut(), 3); /// assert!(!slice.is_empty()); /// ``` #[inline(always)] - #[unstable(feature = "slice_ptr_len", issue = "71146")] - #[rustc_const_unstable(feature = "const_slice_ptr_len", issue = "71146")] + #[stable(feature = "slice_ptr_len", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_slice_ptr_len", since = "CURRENT_RUSTC_VERSION")] pub const fn is_empty(self) -> bool { self.len() == 0 } diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index f0e4b958bc6..96ce3cd3a3f 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -1562,7 +1562,6 @@ impl<T> NonNull<[T]> { /// ``` #[stable(feature = "slice_ptr_len_nonnull", since = "1.63.0")] #[rustc_const_stable(feature = "const_slice_ptr_len_nonnull", since = "1.63.0")] - #[rustc_allow_const_fn_unstable(const_slice_ptr_len)] #[must_use] #[inline] pub const fn len(self) -> usize { @@ -1574,14 +1573,16 @@ impl<T> NonNull<[T]> { /// # Examples /// /// ```rust - /// #![feature(slice_ptr_is_empty_nonnull)] /// use std::ptr::NonNull; /// /// let slice: NonNull<[i8]> = NonNull::slice_from_raw_parts(NonNull::dangling(), 3); /// assert!(!slice.is_empty()); /// ``` - #[unstable(feature = "slice_ptr_is_empty_nonnull", issue = "71146")] - #[rustc_const_unstable(feature = "const_slice_ptr_is_empty_nonnull", issue = "71146")] + #[stable(feature = "slice_ptr_is_empty_nonnull", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable( + feature = "const_slice_ptr_is_empty_nonnull", + since = "CURRENT_RUSTC_VERSION" + )] #[must_use] #[inline] pub const fn is_empty(self) -> bool { diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 0c87860096f..e741149e7ce 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -54,7 +54,6 @@ #![feature(sort_internals)] #![feature(slice_take)] #![feature(slice_from_ptr_range)] -#![feature(slice_ptr_len)] #![feature(slice_split_once)] #![feature(split_as_slice)] #![feature(maybe_uninit_fill)] diff --git a/library/panic_unwind/src/lib.rs b/library/panic_unwind/src/lib.rs index dde1c64c6f1..b0245de501e 100644 --- a/library/panic_unwind/src/lib.rs +++ b/library/panic_unwind/src/lib.rs @@ -19,6 +19,8 @@ #![feature(panic_unwind)] #![feature(staged_api)] #![feature(std_internals)] +#![feature(strict_provenance)] +#![feature(exposed_provenance)] #![feature(rustc_attrs)] #![panic_runtime] #![feature(panic_runtime)] diff --git a/library/panic_unwind/src/seh.rs b/library/panic_unwind/src/seh.rs index e63749c77ce..6bc0559c88f 100644 --- a/library/panic_unwind/src/seh.rs +++ b/library/panic_unwind/src/seh.rs @@ -109,58 +109,88 @@ struct Exception { // [1]: https://www.geoffchappell.com/studies/msvc/language/predefined/ #[cfg(target_arch = "x86")] -#[macro_use] mod imp { - pub type ptr_t = *mut u8; - - macro_rules! ptr { - (0) => { - core::ptr::null_mut() - }; - ($e:expr) => { - $e as *mut u8 - }; + #[repr(transparent)] + #[derive(Copy, Clone)] + pub struct ptr_t(*mut u8); + + impl ptr_t { + pub const fn null() -> Self { + Self(core::ptr::null_mut()) + } + + pub const fn new(ptr: *mut u8) -> Self { + Self(ptr) + } + + pub const fn raw(self) -> *mut u8 { + self.0 + } } } #[cfg(not(target_arch = "x86"))] -#[macro_use] mod imp { - pub type ptr_t = u32; + use core::ptr::addr_of; + + // On 64-bit systems, SEH represents pointers as 32-bit offsets from `__ImageBase`. + #[repr(transparent)] + #[derive(Copy, Clone)] + pub struct ptr_t(u32); extern "C" { pub static __ImageBase: u8; } - macro_rules! ptr { - (0) => (0); - ($e:expr) => { - (($e as usize) - (addr_of!(imp::__ImageBase) as usize)) as u32 + impl ptr_t { + pub const fn null() -> Self { + Self(0) + } + + pub fn new(ptr: *mut u8) -> Self { + // We need to expose the provenance of the pointer because it is not carried by + // the `u32`, while the FFI needs to have this provenance to excess our statics. + // + // NOTE(niluxv): we could use `MaybeUninit<u32>` instead to leak the provenance + // into the FFI. In theory then the other side would need to do some processing + // to get a pointer with correct provenance, but these system functions aren't + // going to be cross-lang LTOed anyway. However, using expose is shorter and + // requires less unsafe. + let addr: usize = ptr.expose_provenance(); + let image_base = unsafe { addr_of!(__ImageBase) }.addr(); + let offset: usize = addr - image_base; + Self(offset as u32) + } + + pub const fn raw(self) -> u32 { + self.0 } } } +use imp::ptr_t; + #[repr(C)] pub struct _ThrowInfo { pub attributes: c_uint, - pub pmfnUnwind: imp::ptr_t, - pub pForwardCompat: imp::ptr_t, - pub pCatchableTypeArray: imp::ptr_t, + pub pmfnUnwind: ptr_t, + pub pForwardCompat: ptr_t, + pub pCatchableTypeArray: ptr_t, } #[repr(C)] pub struct _CatchableTypeArray { pub nCatchableTypes: c_int, - pub arrayOfCatchableTypes: [imp::ptr_t; 1], + pub arrayOfCatchableTypes: [ptr_t; 1], } #[repr(C)] pub struct _CatchableType { pub properties: c_uint, - pub pType: imp::ptr_t, + pub pType: ptr_t, pub thisDisplacement: _PMD, pub sizeOrOffset: c_int, - pub copyFunction: imp::ptr_t, + pub copyFunction: ptr_t, } #[repr(C)] @@ -186,20 +216,20 @@ const TYPE_NAME: [u8; 11] = *b"rust_panic\0"; static mut THROW_INFO: _ThrowInfo = _ThrowInfo { attributes: 0, - pmfnUnwind: ptr!(0), - pForwardCompat: ptr!(0), - pCatchableTypeArray: ptr!(0), + pmfnUnwind: ptr_t::null(), + pForwardCompat: ptr_t::null(), + pCatchableTypeArray: ptr_t::null(), }; static mut CATCHABLE_TYPE_ARRAY: _CatchableTypeArray = - _CatchableTypeArray { nCatchableTypes: 1, arrayOfCatchableTypes: [ptr!(0)] }; + _CatchableTypeArray { nCatchableTypes: 1, arrayOfCatchableTypes: [ptr_t::null()] }; static mut CATCHABLE_TYPE: _CatchableType = _CatchableType { properties: 0, - pType: ptr!(0), + pType: ptr_t::null(), thisDisplacement: _PMD { mdisp: 0, pdisp: -1, vdisp: 0 }, sizeOrOffset: mem::size_of::<Exception>() as c_int, - copyFunction: ptr!(0), + copyFunction: ptr_t::null(), }; extern "C" { @@ -246,9 +276,9 @@ macro_rules! define_cleanup { super::__rust_drop_panic(); } } - unsafe extern $abi2 fn exception_copy(_dest: *mut Exception, - _src: *mut Exception) - -> *mut Exception { + unsafe extern $abi2 fn exception_copy( + _dest: *mut Exception, _src: *mut Exception + ) -> *mut Exception { panic!("Rust panics cannot be copied"); } } @@ -296,24 +326,24 @@ pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 { // In any case, we basically need to do something like this until we can // express more operations in statics (and we may never be able to). atomic_store_seqcst( - addr_of_mut!(THROW_INFO.pmfnUnwind) as *mut u32, - ptr!(exception_cleanup) as u32, + addr_of_mut!(THROW_INFO.pmfnUnwind).cast(), + ptr_t::new(exception_cleanup as *mut u8).raw(), ); atomic_store_seqcst( - addr_of_mut!(THROW_INFO.pCatchableTypeArray) as *mut u32, - ptr!(addr_of!(CATCHABLE_TYPE_ARRAY)) as u32, + addr_of_mut!(THROW_INFO.pCatchableTypeArray).cast(), + ptr_t::new(addr_of_mut!(CATCHABLE_TYPE_ARRAY).cast()).raw(), ); atomic_store_seqcst( - addr_of_mut!(CATCHABLE_TYPE_ARRAY.arrayOfCatchableTypes[0]) as *mut u32, - ptr!(addr_of!(CATCHABLE_TYPE)) as u32, + addr_of_mut!(CATCHABLE_TYPE_ARRAY.arrayOfCatchableTypes[0]).cast(), + ptr_t::new(addr_of_mut!(CATCHABLE_TYPE).cast()).raw(), ); atomic_store_seqcst( - addr_of_mut!(CATCHABLE_TYPE.pType) as *mut u32, - ptr!(addr_of!(TYPE_DESCRIPTOR)) as u32, + addr_of_mut!(CATCHABLE_TYPE.pType).cast(), + ptr_t::new(addr_of_mut!(TYPE_DESCRIPTOR).cast()).raw(), ); atomic_store_seqcst( - addr_of_mut!(CATCHABLE_TYPE.copyFunction) as *mut u32, - ptr!(exception_copy) as u32, + addr_of_mut!(CATCHABLE_TYPE.copyFunction).cast(), + ptr_t::new(exception_copy as *mut u8).raw(), ); extern "system-unwind" { diff --git a/library/proc_macro/src/bridge/client.rs b/library/proc_macro/src/bridge/client.rs index f3cfc41bac7..faca745e56f 100644 --- a/library/proc_macro/src/bridge/client.rs +++ b/library/proc_macro/src/bridge/client.rs @@ -283,7 +283,11 @@ fn maybe_install_panic_hook(force_show_panics: bool) { HIDE_PANICS_DURING_EXPANSION.call_once(|| { let prev = panic::take_hook(); panic::set_hook(Box::new(move |info| { - if force_show_panics || !is_available() { + // We normally report panics by catching unwinds and passing the payload from the + // unwind back to the compiler, but if the panic doesn't unwind we'll abort before + // the compiler has a chance to print an error. So we special-case PanicInfo where + // can_unwind is false. + if force_show_panics || !is_available() || !info.can_unwind() { prev(info) } })); diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index 01c449563ee..a3ebef45c88 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -30,6 +30,7 @@ #![feature(maybe_uninit_write_slice)] #![feature(negative_impls)] #![feature(new_uninit)] +#![feature(panic_can_unwind)] #![feature(restricted_std)] #![feature(rustc_attrs)] #![feature(min_specialization)] diff --git a/library/std/src/f128.rs b/library/std/src/f128.rs new file mode 100644 index 00000000000..4710d7c50b4 --- /dev/null +++ b/library/std/src/f128.rs @@ -0,0 +1,11 @@ +//! Constants for the `f128` double-precision floating point type. +//! +//! *[See also the `f128` primitive type](primitive@f128).* +//! +//! Mathematically significant numbers are provided in the `consts` sub-module. + +#[cfg(test)] +mod tests; + +#[unstable(feature = "f128", issue = "116909")] +pub use core::f128::consts; diff --git a/library/std/src/f128/tests.rs b/library/std/src/f128/tests.rs new file mode 100644 index 00000000000..b64c7f856a1 --- /dev/null +++ b/library/std/src/f128/tests.rs @@ -0,0 +1,40 @@ +#![allow(dead_code)] // FIXME(f16_f128): remove once constants are used + +/// Smallest number +const TINY_BITS: u128 = 0x1; +/// Next smallest number +const TINY_UP_BITS: u128 = 0x2; +/// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0 +const MAX_DOWN_BITS: u128 = 0x7ffeffffffffffffffffffffffffffff; +/// Zeroed exponent, full significant +const LARGEST_SUBNORMAL_BITS: u128 = 0x0000ffffffffffffffffffffffffffff; +/// Exponent = 0b1, zeroed significand +const SMALLEST_NORMAL_BITS: u128 = 0x00010000000000000000000000000000; +/// First pattern over the mantissa +const NAN_MASK1: u128 = 0x0000aaaaaaaaaaaaaaaaaaaaaaaaaaaa; +/// Second pattern over the mantissa +const NAN_MASK2: u128 = 0x00005555555555555555555555555555; + +/// Compare by value +#[allow(unused_macros)] +macro_rules! assert_f128_eq { + ($a:expr, $b:expr) => { + let (l, r): (&f128, &f128) = (&$a, &$b); + assert_eq!(*l, *r, "\na: {:#0130x}\nb: {:#0130x}", l.to_bits(), r.to_bits()) + }; +} + +/// Compare by representation +#[allow(unused_macros)] +macro_rules! assert_f128_biteq { + ($a:expr, $b:expr) => { + let (l, r): (&f128, &f128) = (&$a, &$b); + let lb = l.to_bits(); + let rb = r.to_bits(); + assert_eq!( + lb, rb, + "float {:?} is not bitequal to {:?}.\na: {:#0130x}\nb: {:#0130x}", + *l, *r, lb, rb + ); + }; +} diff --git a/library/std/src/f16.rs b/library/std/src/f16.rs new file mode 100644 index 00000000000..c36f9f5d4c6 --- /dev/null +++ b/library/std/src/f16.rs @@ -0,0 +1,11 @@ +//! Constants for the `f16` double-precision floating point type. +//! +//! *[See also the `f16` primitive type](primitive@f16).* +//! +//! Mathematically significant numbers are provided in the `consts` sub-module. + +#[cfg(test)] +mod tests; + +#[unstable(feature = "f16", issue = "116909")] +pub use core::f16::consts; diff --git a/library/std/src/f16/tests.rs b/library/std/src/f16/tests.rs new file mode 100644 index 00000000000..d65c43eca4b --- /dev/null +++ b/library/std/src/f16/tests.rs @@ -0,0 +1,46 @@ +#![allow(dead_code)] // FIXME(f16_f128): remove once constants are used + +// We run out of precision pretty quickly with f16 +const F16_APPROX_L1: f16 = 0.001; +const F16_APPROX_L2: f16 = 0.01; +const F16_APPROX_L3: f16 = 0.1; +const F16_APPROX_L4: f16 = 0.5; + +/// Smallest number +const TINY_BITS: u16 = 0x1; +/// Next smallest number +const TINY_UP_BITS: u16 = 0x2; +/// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0 +const MAX_DOWN_BITS: u16 = 0x7bfe; +/// Zeroed exponent, full significant +const LARGEST_SUBNORMAL_BITS: u16 = 0x03ff; +/// Exponent = 0b1, zeroed significand +const SMALLEST_NORMAL_BITS: u16 = 0x0400; +/// First pattern over the mantissa +const NAN_MASK1: u16 = 0x02aa; +/// Second pattern over the mantissa +const NAN_MASK2: u16 = 0x0155; + +/// Compare by value +#[allow(unused_macros)] +macro_rules! assert_f16_eq { + ($a:expr, $b:expr) => { + let (l, r): (&f16, &f16) = (&$a, &$b); + assert_eq!(*l, *r, "\na: {:#018x}\nb: {:#018x}", l.to_bits(), r.to_bits()) + }; +} + +/// Compare by representation +#[allow(unused_macros)] +macro_rules! assert_f16_biteq { + ($a:expr, $b:expr) => { + let (l, r): (&f16, &f16) = (&$a, &$b); + let lb = l.to_bits(); + let rb = r.to_bits(); + assert_eq!( + lb, rb, + "float {:?} is not bitequal to {:?}.\na: {:#018x}\nb: {:#018x}", + *l, *r, lb, rb + ); + }; +} diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index b1102b440e0..1b8fafd0089 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -465,14 +465,20 @@ impl File { OpenOptions::new() } - /// Attempts to sync all OS-internal metadata to disk. + /// Attempts to sync all OS-internal file content and metadata to disk. /// /// This function will attempt to ensure that all in-memory data reaches the /// filesystem before returning. /// /// This can be used to handle errors that would otherwise only be caught - /// when the `File` is closed. Dropping a file will ignore errors in - /// synchronizing this in-memory data. + /// when the `File` is closed, as dropping a `File` will ignore all errors. + /// Note, however, that `sync_all` is generally more expensive than closing + /// a file by dropping it, because the latter is not required to block until + /// the data has been written to the filesystem. + /// + /// If synchronizing the metadata is not required, use [`sync_data`] instead. + /// + /// [`sync_data`]: File::sync_data /// /// # Examples /// @@ -489,6 +495,7 @@ impl File { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[doc(alias = "fsync")] pub fn sync_all(&self) -> io::Result<()> { self.inner.fsync() } @@ -520,6 +527,7 @@ impl File { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[doc(alias = "fdatasync")] pub fn sync_data(&self) -> io::Result<()> { self.inner.datasync() } diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs index 250cd583696..0cdc49c87d8 100644 --- a/library/std/src/io/buffered/bufreader.rs +++ b/library/std/src/io/buffered/bufreader.rs @@ -383,12 +383,7 @@ impl<R: ?Sized + Read> Read for BufReader<R> { // buffer. let mut bytes = Vec::new(); self.read_to_end(&mut bytes)?; - let string = crate::str::from_utf8(&bytes).map_err(|_| { - io::const_io_error!( - io::ErrorKind::InvalidData, - "stream did not contain valid UTF-8", - ) - })?; + let string = crate::str::from_utf8(&bytes).map_err(|_| io::Error::INVALID_UTF8)?; *buf += string; Ok(string.len()) } diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 85625116d02..4e1d7c2ac65 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -75,6 +75,30 @@ impl fmt::Debug for Error { } } +/// Common errors constants for use in std +#[allow(dead_code)] +impl Error { + pub(crate) const INVALID_UTF8: Self = + const_io_error!(ErrorKind::InvalidData, "stream did not contain valid UTF-8"); + + pub(crate) const READ_EXACT_EOF: Self = + const_io_error!(ErrorKind::UnexpectedEof, "failed to fill whole buffer"); + + pub(crate) const UNKNOWN_THREAD_COUNT: Self = const_io_error!( + ErrorKind::NotFound, + "The number of hardware threads is not known for the target platform" + ); + + pub(crate) const UNSUPPORTED_PLATFORM: Self = + const_io_error!(ErrorKind::Unsupported, "operation not supported on this platform"); + + pub(crate) const WRITE_ALL_EOF: Self = + const_io_error!(ErrorKind::WriteZero, "failed to write whole buffer"); + + pub(crate) const ZERO_TIMEOUT: Self = + const_io_error!(ErrorKind::InvalidInput, "cannot set a 0 duration timeout"); +} + #[stable(feature = "rust1", since = "1.0.0")] impl From<alloc::ffi::NulError> for Error { /// Converts a [`alloc::ffi::NulError`] into a [`Error`]. diff --git a/library/std/src/io/impls.rs b/library/std/src/io/impls.rs index ee7ed4bcc9a..dd7e0725176 100644 --- a/library/std/src/io/impls.rs +++ b/library/std/src/io/impls.rs @@ -5,9 +5,7 @@ use crate::alloc::Allocator; use crate::cmp; use crate::collections::VecDeque; use crate::fmt; -use crate::io::{ - self, BorrowedCursor, BufRead, ErrorKind, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write, -}; +use crate::io::{self, BorrowedCursor, BufRead, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write}; use crate::mem; use crate::str; @@ -289,10 +287,7 @@ impl Read for &[u8] { #[inline] fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { if buf.len() > self.len() { - return Err(io::const_io_error!( - ErrorKind::UnexpectedEof, - "failed to fill whole buffer" - )); + return Err(io::Error::READ_EXACT_EOF); } let (a, b) = self.split_at(buf.len()); @@ -312,10 +307,7 @@ impl Read for &[u8] { #[inline] fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { if cursor.capacity() > self.len() { - return Err(io::const_io_error!( - ErrorKind::UnexpectedEof, - "failed to fill whole buffer" - )); + return Err(io::Error::READ_EXACT_EOF); } let (a, b) = self.split_at(cursor.capacity()); @@ -336,9 +328,7 @@ impl Read for &[u8] { #[inline] fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> { - let content = str::from_utf8(self).map_err(|_| { - io::const_io_error!(ErrorKind::InvalidData, "stream did not contain valid UTF-8") - })?; + let content = str::from_utf8(self).map_err(|_| io::Error::INVALID_UTF8)?; buf.push_str(content); let len = self.len(); *self = &self[len..]; @@ -399,11 +389,7 @@ impl Write for &mut [u8] { #[inline] fn write_all(&mut self, data: &[u8]) -> io::Result<()> { - if self.write(data)? == data.len() { - Ok(()) - } else { - Err(io::const_io_error!(ErrorKind::WriteZero, "failed to write whole buffer")) - } + if self.write(data)? == data.len() { Ok(()) } else { Err(io::Error::WRITE_ALL_EOF) } } #[inline] @@ -491,9 +477,7 @@ impl<A: Allocator> Read for VecDeque<u8, A> { // middle of an UTF-8 character. let len = self.len(); let content = self.make_contiguous(); - let string = str::from_utf8(content).map_err(|_| { - io::const_io_error!(ErrorKind::InvalidData, "stream did not contain valid UTF-8") - })?; + let string = str::from_utf8(content).map_err(|_| io::Error::INVALID_UTF8)?; buf.push_str(string); self.clear(); Ok(len) diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 980b3e7aa48..98973a43e1d 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -311,14 +311,14 @@ pub use self::buffered::WriterPanicked; #[unstable(feature = "raw_os_error_ty", issue = "107792")] pub use self::error::RawOsError; pub(crate) use self::stdio::attempt_print_to_stderr; -#[unstable(feature = "internal_output_capture", issue = "none")] -#[doc(no_inline, hidden)] -pub use self::stdio::set_output_capture; #[stable(feature = "is_terminal", since = "1.70.0")] pub use self::stdio::IsTerminal; #[unstable(feature = "print_internals", issue = "none")] #[doc(hidden)] pub use self::stdio::{_eprint, _print}; +#[unstable(feature = "internal_output_capture", issue = "none")] +#[doc(no_inline, hidden)] +pub use self::stdio::{set_output_capture, try_set_output_capture}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::{ buffered::{BufReader, BufWriter, IntoInnerError, LineWriter}, @@ -385,12 +385,7 @@ where let mut g = Guard { len: buf.len(), buf: buf.as_mut_vec() }; let ret = f(g.buf); if str::from_utf8(&g.buf[g.len..]).is_err() { - ret.and_then(|_| { - Err(error::const_io_error!( - ErrorKind::InvalidData, - "stream did not contain valid UTF-8" - )) - }) + ret.and_then(|_| Err(Error::INVALID_UTF8)) } else { g.len = g.buf.len(); ret @@ -566,11 +561,7 @@ pub(crate) fn default_read_exact<R: Read + ?Sized>(this: &mut R, mut buf: &mut [ Err(e) => return Err(e), } } - if !buf.is_empty() { - Err(error::const_io_error!(ErrorKind::UnexpectedEof, "failed to fill whole buffer")) - } else { - Ok(()) - } + if !buf.is_empty() { Err(Error::READ_EXACT_EOF) } else { Ok(()) } } pub(crate) fn default_read_buf<F>(read: F, mut cursor: BorrowedCursor<'_>) -> Result<()> @@ -595,10 +586,7 @@ pub(crate) fn default_read_buf_exact<R: Read + ?Sized>( } if cursor.written() == prev_written { - return Err(error::const_io_error!( - ErrorKind::UnexpectedEof, - "failed to fill whole buffer" - )); + return Err(Error::READ_EXACT_EOF); } } @@ -1709,10 +1697,7 @@ pub trait Write { while !buf.is_empty() { match self.write(buf) { Ok(0) => { - return Err(error::const_io_error!( - ErrorKind::WriteZero, - "failed to write whole buffer", - )); + return Err(Error::WRITE_ALL_EOF); } Ok(n) => buf = &buf[n..], Err(ref e) if e.is_interrupted() => {} @@ -1777,10 +1762,7 @@ pub trait Write { while !bufs.is_empty() { match self.write_vectored(bufs) { Ok(0) => { - return Err(error::const_io_error!( - ErrorKind::WriteZero, - "failed to write whole buffer", - )); + return Err(Error::WRITE_ALL_EOF); } Ok(n) => IoSlice::advance_slices(&mut bufs, n), Err(ref e) if e.is_interrupted() => {} diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index 73fa7cbc3fe..07fa9259e0b 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -15,6 +15,7 @@ use crate::panic::{RefUnwindSafe, UnwindSafe}; use crate::sync::atomic::{AtomicBool, Ordering}; use crate::sync::{Arc, Mutex, MutexGuard, OnceLock, ReentrantLock, ReentrantLockGuard}; use crate::sys::stdio; +use crate::thread::AccessError; type LocalStream = Arc<Mutex<Vec<u8>>>; @@ -1064,12 +1065,31 @@ impl fmt::Debug for StderrLock<'_> { )] #[doc(hidden)] pub fn set_output_capture(sink: Option<LocalStream>) -> Option<LocalStream> { + try_set_output_capture(sink).expect( + "cannot access a Thread Local Storage value \ + during or after destruction", + ) +} + +/// Tries to set the thread-local output capture buffer and returns the old one. +/// This may fail once thread-local destructors are called. It's used in panic +/// handling instead of `set_output_capture`. +#[unstable( + feature = "internal_output_capture", + reason = "this function is meant for use in the test crate \ + and may disappear in the future", + issue = "none" +)] +#[doc(hidden)] +pub fn try_set_output_capture( + sink: Option<LocalStream>, +) -> Result<Option<LocalStream>, AccessError> { if sink.is_none() && !OUTPUT_CAPTURE_USED.load(Ordering::Relaxed) { // OUTPUT_CAPTURE is definitely None since OUTPUT_CAPTURE_USED is false. - return None; + return Ok(None); } OUTPUT_CAPTURE_USED.store(true, Ordering::Relaxed); - OUTPUT_CAPTURE.with(move |slot| slot.replace(sink)) + OUTPUT_CAPTURE.try_with(move |slot| slot.replace(sink)) } /// Write `args` to the capture buffer if enabled and possible, or `global_s` diff --git a/library/std/src/io/tests.rs b/library/std/src/io/tests.rs index eb5d5988768..090a091b09a 100644 --- a/library/std/src/io/tests.rs +++ b/library/std/src/io/tests.rs @@ -210,6 +210,15 @@ fn read_buf_exact() { } #[test] +#[should_panic] +fn borrowed_cursor_advance_overflow() { + let mut buf = [0; 512]; + let mut buf = BorrowedBuf::from(&mut buf[..]); + buf.unfilled().advance(1); + buf.unfilled().advance(usize::MAX); +} + +#[test] fn take_eof() { struct R; diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index c713eefc72c..e9de3b77670 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -214,7 +214,16 @@ //! [slice]: prim@slice #![cfg_attr(not(feature = "restricted-std"), stable(feature = "rust1", since = "1.0.0"))] -#![cfg_attr(feature = "restricted-std", unstable(feature = "restricted_std", issue = "none"))] +#![cfg_attr( + feature = "restricted-std", + unstable( + feature = "restricted_std", + issue = "none", + reason = "You have attempted to use a standard library built for a platform that it doesn't \ + know how to support. Consider building it for a known environment, disabling it with \ + `#![no_std]` or overriding this warning by enabling this feature." + ) +)] #![cfg_attr(not(bootstrap), rustc_preserve_ub_checks)] #![doc( html_playground_url = "https://play.rust-lang.org/", @@ -256,7 +265,6 @@ feature(slice_index_methods, coerce_unsized, sgx_platform) )] #![cfg_attr(any(windows, target_os = "uefi"), feature(round_char_boundary))] -#![cfg_attr(target_os = "xous", feature(slice_ptr_len))] #![cfg_attr(target_family = "wasm", feature(stdarch_wasm_atomic_wait))] #![cfg_attr( all(any(target_arch = "x86_64", target_arch = "x86"), target_os = "uefi"), @@ -283,6 +291,8 @@ #![feature(doc_masked)] #![feature(doc_notable_trait)] #![feature(dropck_eyepatch)] +#![feature(f128)] +#![feature(f16)] #![feature(if_let_guard)] #![feature(intra_doc_pointers)] #![feature(lang_items)] @@ -558,6 +568,10 @@ pub use core::u8; #[allow(deprecated, deprecated_in_future)] pub use core::usize; +#[unstable(feature = "f128", issue = "116909")] +pub mod f128; +#[unstable(feature = "f16", issue = "116909")] +pub mod f16; pub mod f32; pub mod f64; diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs index 0b3c5153871..010ce4e5076 100644 --- a/library/std/src/os/fd/owned.rs +++ b/library/std/src/os/fd/owned.rs @@ -117,10 +117,7 @@ impl BorrowedFd<'_> { #[cfg(any(target_arch = "wasm32", target_os = "hermit"))] #[stable(feature = "io_safety", since = "1.63.0")] pub fn try_clone_to_owned(&self) -> crate::io::Result<OwnedFd> { - Err(crate::io::const_io_error!( - crate::io::ErrorKind::Unsupported, - "operation not supported on this platform", - )) + Err(crate::io::Error::UNSUPPORTED_PLATFORM) } } diff --git a/library/std/src/os/unix/fs.rs b/library/std/src/os/unix/fs.rs index 058e9b90cc7..970023d8cf1 100644 --- a/library/std/src/os/unix/fs.rs +++ b/library/std/src/os/unix/fs.rs @@ -127,11 +127,7 @@ pub trait FileExt { Err(e) => return Err(e), } } - if !buf.is_empty() { - Err(io::const_io_error!(io::ErrorKind::UnexpectedEof, "failed to fill whole buffer",)) - } else { - Ok(()) - } + if !buf.is_empty() { Err(io::Error::READ_EXACT_EOF) } else { Ok(()) } } /// Writes a number of bytes starting from a given offset. @@ -249,10 +245,7 @@ pub trait FileExt { while !buf.is_empty() { match self.write_at(buf, offset) { Ok(0) => { - return Err(io::const_io_error!( - io::ErrorKind::WriteZero, - "failed to write whole buffer", - )); + return Err(io::Error::WRITE_ALL_EOF); } Ok(n) => { buf = &buf[n..]; diff --git a/library/std/src/os/unix/net/addr.rs b/library/std/src/os/unix/net/addr.rs index 9757653e02c..1787eba0ef8 100644 --- a/library/std/src/os/unix/net/addr.rs +++ b/library/std/src/os/unix/net/addr.rs @@ -107,6 +107,16 @@ impl SocketAddr { addr: libc::sockaddr_un, mut len: libc::socklen_t, ) -> io::Result<SocketAddr> { + if cfg!(target_os = "openbsd") { + // on OpenBSD, getsockname(2) returns the actual size of the socket address, + // and not the len of the content. Figure out the length for ourselves. + // https://marc.info/?l=openbsd-bugs&m=170105481926736&w=2 + let sun_path: &[u8] = + unsafe { mem::transmute::<&[libc::c_char], &[u8]>(&addr.sun_path) }; + len = core::slice::memchr::memchr(0, sun_path) + .map_or(len, |new_len| (new_len + sun_path_offset(&addr)) as libc::socklen_t); + } + if len == 0 { // When there is a datagram from unnamed unix socket // linux returns zero bytes of address diff --git a/library/std/src/os/wasi/fs.rs b/library/std/src/os/wasi/fs.rs index 4525c3aa914..46fc2a50de9 100644 --- a/library/std/src/os/wasi/fs.rs +++ b/library/std/src/os/wasi/fs.rs @@ -86,11 +86,7 @@ pub trait FileExt { Err(e) => return Err(e), } } - if !buf.is_empty() { - Err(io::const_io_error!(io::ErrorKind::UnexpectedEof, "failed to fill whole buffer")) - } else { - Ok(()) - } + if !buf.is_empty() { Err(io::Error::READ_EXACT_EOF) } else { Ok(()) } } /// Writes a number of bytes starting from a given offset. @@ -153,10 +149,7 @@ pub trait FileExt { while !buf.is_empty() { match self.write_at(buf, offset) { Ok(0) => { - return Err(io::const_io_error!( - io::ErrorKind::WriteZero, - "failed to write whole buffer", - )); + return Err(io::Error::WRITE_ALL_EOF); } Ok(n) => { buf = &buf[n..]; diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index f46e1e171d2..0052fcbb94a 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -24,11 +24,11 @@ use crate::sys_common::backtrace; use crate::thread; #[cfg(not(test))] -use crate::io::set_output_capture; +use crate::io::try_set_output_capture; // make sure to use the stderr output configured // by libtest in the real copy of std #[cfg(test)] -use realstd::io::set_output_capture; +use realstd::io::try_set_output_capture; // Binary interface to the panic runtime that the standard library depends on. // @@ -284,9 +284,9 @@ fn default_hook(info: &PanicInfo<'_>) { } }; - if let Some(local) = set_output_capture(None) { + if let Ok(Some(local)) = try_set_output_capture(None) { write(&mut *local.lock().unwrap_or_else(|e| e.into_inner())); - set_output_capture(Some(local)); + try_set_output_capture(Some(local)).ok(); } else if let Some(mut out) = panic_output() { write(&mut out); } diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 56ea51226f9..5f43d63bf84 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -2143,10 +2143,10 @@ impl Path { /// # Examples /// /// ``` - /// use std::path::Path; + /// use std::path::{Path, PathBuf}; /// /// let path_buf = Path::new("foo.txt").to_path_buf(); - /// assert_eq!(path_buf, std::path::PathBuf::from("foo.txt")); + /// assert_eq!(path_buf, PathBuf::from("foo.txt")); /// ``` #[rustc_conversion_suggestion] #[must_use = "this returns the result of the operation, \ @@ -2278,10 +2278,9 @@ impl Path { /// Produces an iterator over `Path` and its ancestors. /// /// The iterator will yield the `Path` that is returned if the [`parent`] method is used zero - /// or more times. That means, the iterator will yield `&self`, `&self.parent().unwrap()`, - /// `&self.parent().unwrap().parent().unwrap()` and so on. If the [`parent`] method returns - /// [`None`], the iterator will do likewise. The iterator will always yield at least one value, - /// namely `&self`. + /// or more times. If the [`parent`] method returns [`None`], the iterator will do likewise. + /// The iterator will always yield at least one value, namely `Some(&self)`. Next it will yield + /// `&self.parent()`, `&self.parent().and_then(Path::parent)` and so on. /// /// # Examples /// diff --git a/library/std/src/sys/pal/hermit/net.rs b/library/std/src/sys/pal/hermit/net.rs index 1c53796f5d4..23ac71cb9f2 100644 --- a/library/std/src/sys/pal/hermit/net.rs +++ b/library/std/src/sys/pal/hermit/net.rs @@ -80,10 +80,7 @@ impl Socket { let mut pollfd = netc::pollfd { fd: self.as_raw_fd(), events: netc::POLLOUT, revents: 0 }; if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { - return Err(io::const_io_error!( - io::ErrorKind::InvalidInput, - "cannot set a 0 duration timeout", - )); + return Err(io::Error::ZERO_TIMEOUT); } let start = Instant::now(); @@ -245,10 +242,7 @@ impl Socket { let timeout = match dur { Some(dur) => { if dur.as_secs() == 0 && dur.subsec_nanos() == 0 { - return Err(io::const_io_error!( - io::ErrorKind::InvalidInput, - "cannot set a 0 duration timeout", - )); + return Err(io::Error::ZERO_TIMEOUT); } let secs = if dur.as_secs() > netc::time_t::MAX as u64 { diff --git a/library/std/src/sys/pal/sgx/net.rs b/library/std/src/sys/pal/sgx/net.rs index edb28e2300f..68a2d5eded2 100644 --- a/library/std/src/sys/pal/sgx/net.rs +++ b/library/std/src/sys/pal/sgx/net.rs @@ -97,10 +97,7 @@ impl TcpStream { pub fn connect_timeout(addr: &SocketAddr, dur: Duration) -> io::Result<TcpStream> { if dur == Duration::default() { - return Err(io::const_io_error!( - io::ErrorKind::InvalidInput, - "cannot set a 0 duration timeout", - )); + return Err(io::Error::ZERO_TIMEOUT); } Self::connect(Ok(addr)) // FIXME: ignoring timeout } @@ -108,10 +105,7 @@ impl TcpStream { pub fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> { match dur { Some(dur) if dur == Duration::default() => { - return Err(io::const_io_error!( - io::ErrorKind::InvalidInput, - "cannot set a 0 duration timeout", - )); + return Err(io::Error::ZERO_TIMEOUT); } _ => sgx_ineffective(()), } @@ -120,10 +114,7 @@ impl TcpStream { pub fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> { match dur { Some(dur) if dur == Duration::default() => { - return Err(io::const_io_error!( - io::ErrorKind::InvalidInput, - "cannot set a 0 duration timeout", - )); + return Err(io::Error::ZERO_TIMEOUT); } _ => sgx_ineffective(()), } diff --git a/library/std/src/sys/pal/solid/mod.rs b/library/std/src/sys/pal/solid/mod.rs index 109ee1a0ab6..3f6ff37903a 100644 --- a/library/std/src/sys/pal/solid/mod.rs +++ b/library/std/src/sys/pal/solid/mod.rs @@ -50,10 +50,7 @@ pub fn unsupported<T>() -> crate::io::Result<T> { } pub fn unsupported_err() -> crate::io::Error { - crate::io::const_io_error!( - crate::io::ErrorKind::Unsupported, - "operation not supported on this platform", - ) + crate::io::Error::UNSUPPORTED_PLATFORM } #[inline] diff --git a/library/std/src/sys/pal/solid/net.rs b/library/std/src/sys/pal/solid/net.rs index 6ea874e509e..5bd339849e9 100644 --- a/library/std/src/sys/pal/solid/net.rs +++ b/library/std/src/sys/pal/solid/net.rs @@ -154,10 +154,7 @@ impl Socket { } if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { - return Err(io::const_io_error!( - io::ErrorKind::InvalidInput, - "cannot set a 0 duration timeout", - )); + return Err(io::Error::ZERO_TIMEOUT); } let mut timeout = @@ -306,10 +303,7 @@ impl Socket { let timeout = match dur { Some(dur) => { if dur.as_secs() == 0 && dur.subsec_nanos() == 0 { - return Err(io::const_io_error!( - io::ErrorKind::InvalidInput, - "cannot set a 0 duration timeout", - )); + return Err(io::Error::ZERO_TIMEOUT); } let secs = if dur.as_secs() > netc::c_long::MAX as u64 { diff --git a/library/std/src/sys/pal/teeos/thread.rs b/library/std/src/sys/pal/teeos/thread.rs index ae2f58ca08e..f4723b2ea46 100644 --- a/library/std/src/sys/pal/teeos/thread.rs +++ b/library/std/src/sys/pal/teeos/thread.rs @@ -141,10 +141,7 @@ impl Drop for Thread { // Note: Both `sched_getaffinity` and `sysconf` are available but not functional on // teeos, so this function always returns an Error! pub fn available_parallelism() -> io::Result<NonZero<usize>> { - Err(io::Error::new( - io::ErrorKind::NotFound, - "The number of hardware threads is not known for the target platform", - )) + Err(io::Error::UNKNOWN_THREAD_COUNT) } fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index 4ae76518c4f..3ef43a923a3 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -433,6 +433,6 @@ mod unsupported { } pub fn unsupported_err() -> io::Error { - io::const_io_error!(io::ErrorKind::Unsupported, "operation not supported on this platform",) + io::Error::UNSUPPORTED_PLATFORM } } diff --git a/library/std/src/sys/pal/unix/net.rs b/library/std/src/sys/pal/unix/net.rs index 9a0a1b18aee..7237989c905 100644 --- a/library/std/src/sys/pal/unix/net.rs +++ b/library/std/src/sys/pal/unix/net.rs @@ -175,10 +175,7 @@ impl Socket { let mut pollfd = libc::pollfd { fd: self.as_raw_fd(), events: libc::POLLOUT, revents: 0 }; if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { - return Err(io::const_io_error!( - io::ErrorKind::InvalidInput, - "cannot set a 0 duration timeout", - )); + return Err(io::Error::ZERO_TIMEOUT); } let start = Instant::now(); @@ -360,10 +357,7 @@ impl Socket { let timeout = match dur { Some(dur) => { if dur.as_secs() == 0 && dur.subsec_nanos() == 0 { - return Err(io::const_io_error!( - io::ErrorKind::InvalidInput, - "cannot set a 0 duration timeout", - )); + return Err(io::Error::ZERO_TIMEOUT); } let secs = if dur.as_secs() > libc::time_t::MAX as u64 { diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs index c550cc04a39..6a6bfc77a85 100644 --- a/library/std/src/sys/pal/unix/thread.rs +++ b/library/std/src/sys/pal/unix/thread.rs @@ -356,7 +356,7 @@ pub fn available_parallelism() -> io::Result<NonZero<usize>> { } match unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) } { -1 => Err(io::Error::last_os_error()), - 0 => Err(io::const_io_error!(io::ErrorKind::NotFound, "The number of hardware threads is not known for the target platform")), + 0 => Err(io::Error::UNKNOWN_THREAD_COUNT), cpus => { let count = cpus as usize; // Cover the unusual situation where we were able to get the quota but not the affinity mask @@ -439,7 +439,7 @@ pub fn available_parallelism() -> io::Result<NonZero<usize>> { if res == -1 { return Err(io::Error::last_os_error()); } else if cpus == 0 { - return Err(io::const_io_error!(io::ErrorKind::NotFound, "The number of hardware threads is not known for the target platform")); + return Err(io::Error::UNKNOWN_THREAD_COUNT); } } @@ -452,13 +452,13 @@ pub fn available_parallelism() -> io::Result<NonZero<usize>> { } else { let cpus = (*_syspage_ptr).num_cpu; NonZero::new(cpus as usize) - .ok_or(io::const_io_error!(io::ErrorKind::NotFound, "The number of hardware threads is not known for the target platform")) + .ok_or(io::Error::UNKNOWN_THREAD_COUNT) } } } else if #[cfg(any(target_os = "solaris", target_os = "illumos"))] { let mut cpus = 0u32; if unsafe { libc::pset_info(libc::PS_MYID, core::ptr::null_mut(), &mut cpus, core::ptr::null_mut()) } != 0 { - return Err(io::const_io_error!(io::ErrorKind::NotFound, "The number of hardware threads is not known for the target platform")); + return Err(io::Error::UNKNOWN_THREAD_COUNT); } Ok(unsafe { NonZero::new_unchecked(cpus as usize) }) } else if #[cfg(target_os = "haiku")] { @@ -469,7 +469,7 @@ pub fn available_parallelism() -> io::Result<NonZero<usize>> { let res = libc::get_system_info(&mut sinfo); if res != libc::B_OK { - return Err(io::const_io_error!(io::ErrorKind::NotFound, "The number of hardware threads is not known for the target platform")); + return Err(io::Error::UNKNOWN_THREAD_COUNT); } Ok(NonZero::new_unchecked(sinfo.cpu_count as usize)) diff --git a/library/std/src/sys/pal/unsupported/common.rs b/library/std/src/sys/pal/unsupported/common.rs index 4f44db610af..76f80291f0e 100644 --- a/library/std/src/sys/pal/unsupported/common.rs +++ b/library/std/src/sys/pal/unsupported/common.rs @@ -13,10 +13,7 @@ pub fn unsupported<T>() -> std_io::Result<T> { } pub fn unsupported_err() -> std_io::Error { - std_io::const_io_error!( - std_io::ErrorKind::Unsupported, - "operation not supported on this platform", - ) + std_io::Error::UNSUPPORTED_PLATFORM } pub fn is_interrupted(_code: i32) -> bool { diff --git a/library/std/src/sys/pal/windows/net.rs b/library/std/src/sys/pal/windows/net.rs index 1e6169ea8ec..9e15b15a351 100644 --- a/library/std/src/sys/pal/windows/net.rs +++ b/library/std/src/sys/pal/windows/net.rs @@ -154,10 +154,7 @@ impl Socket { match result { Err(ref error) if error.kind() == io::ErrorKind::WouldBlock => { if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { - return Err(io::const_io_error!( - io::ErrorKind::InvalidInput, - "cannot set a 0 duration timeout", - )); + return Err(io::Error::ZERO_TIMEOUT); } let mut timeout = c::timeval { @@ -364,10 +361,7 @@ impl Socket { Some(dur) => { let timeout = sys::dur2timeout(dur); if timeout == 0 { - return Err(io::const_io_error!( - io::ErrorKind::InvalidInput, - "cannot set a 0 duration timeout", - )); + return Err(io::Error::ZERO_TIMEOUT); } timeout } diff --git a/library/std/src/sys/pal/windows/thread.rs b/library/std/src/sys/pal/windows/thread.rs index 9b1c5b34bbf..70099e0a3b5 100644 --- a/library/std/src/sys/pal/windows/thread.rs +++ b/library/std/src/sys/pal/windows/thread.rs @@ -45,13 +45,11 @@ impl Thread { Err(io::Error::last_os_error()) }; - extern "system" fn thread_start(main: *mut c_void) -> c::DWORD { - unsafe { - // Next, reserve some stack space for if we otherwise run out of stack. - stack_overflow::reserve_stack(); - // Finally, let's run some code. - Box::from_raw(main as *mut Box<dyn FnOnce()>)(); - } + unsafe extern "system" fn thread_start(main: *mut c_void) -> c::DWORD { + // Next, reserve some stack space for if we otherwise run out of stack. + stack_overflow::reserve_stack(); + // Finally, let's run some code. + Box::from_raw(main as *mut Box<dyn FnOnce()>)(); 0 } } @@ -59,15 +57,19 @@ impl Thread { pub fn set_name(name: &CStr) { if let Ok(utf8) = name.to_str() { if let Ok(utf16) = to_u16s(utf8) { - Self::set_name_wide(&utf16) + unsafe { + // SAFETY: the vec returned by `to_u16s` ends with a zero value + Self::set_name_wide(&utf16) + } }; }; } - pub fn set_name_wide(name: &[u16]) { - unsafe { - c::SetThreadDescription(c::GetCurrentThread(), name.as_ptr()); - }; + /// # Safety + /// + /// `name` must end with a zero value + pub unsafe fn set_name_wide(name: &[u16]) { + c::SetThreadDescription(c::GetCurrentThread(), name.as_ptr()); } pub fn join(self) { @@ -116,10 +118,7 @@ pub fn available_parallelism() -> io::Result<NonZero<usize>> { sysinfo.dwNumberOfProcessors as usize }; match res { - 0 => Err(io::const_io_error!( - io::ErrorKind::NotFound, - "The number of hardware threads is not known for the target platform", - )), + 0 => Err(io::Error::UNKNOWN_THREAD_COUNT), cpus => Ok(unsafe { NonZero::new_unchecked(cpus) }), } } diff --git a/library/std/src/sys/pal/xous/net/tcpstream.rs b/library/std/src/sys/pal/xous/net/tcpstream.rs index aebef02acda..0ad88110711 100644 --- a/library/std/src/sys/pal/xous/net/tcpstream.rs +++ b/library/std/src/sys/pal/xous/net/tcpstream.rs @@ -140,10 +140,7 @@ impl TcpStream { pub fn set_read_timeout(&self, timeout: Option<Duration>) -> io::Result<()> { if let Some(to) = timeout { if to.is_zero() { - return Err(io::const_io_error!( - io::ErrorKind::InvalidInput, - &"Zero is an invalid timeout", - )); + return Err(io::Error::ZERO_TIMEOUT); } } self.read_timeout.store( @@ -156,10 +153,7 @@ impl TcpStream { pub fn set_write_timeout(&self, timeout: Option<Duration>) -> io::Result<()> { if let Some(to) = timeout { if to.is_zero() { - return Err(io::const_io_error!( - io::ErrorKind::InvalidInput, - &"Zero is an invalid timeout", - )); + return Err(io::Error::ZERO_TIMEOUT); } } self.write_timeout.store( diff --git a/library/std/src/sys/pal/xous/net/udp.rs b/library/std/src/sys/pal/xous/net/udp.rs index cafa5b3bde8..3d0522b25f3 100644 --- a/library/std/src/sys/pal/xous/net/udp.rs +++ b/library/std/src/sys/pal/xous/net/udp.rs @@ -331,10 +331,7 @@ impl UdpSocket { pub fn set_read_timeout(&self, timeout: Option<Duration>) -> io::Result<()> { if let Some(d) = timeout { if d.is_zero() { - return Err(io::const_io_error!( - io::ErrorKind::InvalidInput, - &"Zero duration is invalid" - )); + return Err(io::Error::ZERO_TIMEOUT); } } self.read_timeout @@ -345,10 +342,7 @@ impl UdpSocket { pub fn set_write_timeout(&self, timeout: Option<Duration>) -> io::Result<()> { if let Some(d) = timeout { if d.is_zero() { - return Err(io::const_io_error!( - io::ErrorKind::InvalidInput, - &"Zero duration is invalid" - )); + return Err(io::Error::ZERO_TIMEOUT); } } self.write_timeout diff --git a/library/std/src/sys/pal/zkvm/mod.rs b/library/std/src/sys/pal/zkvm/mod.rs index 228a976dbab..4f79f8c4961 100644 --- a/library/std/src/sys/pal/zkvm/mod.rs +++ b/library/std/src/sys/pal/zkvm/mod.rs @@ -12,8 +12,6 @@ const WORD_SIZE: usize = core::mem::size_of::<u32>(); pub mod alloc; #[path = "../zkvm/args.rs"] pub mod args; -#[path = "../unix/cmath.rs"] -pub mod cmath; pub mod env; #[path = "../unsupported/fs.rs"] pub mod fs; @@ -54,10 +52,7 @@ pub fn unsupported<T>() -> std_io::Result<T> { } pub fn unsupported_err() -> std_io::Error { - std_io::const_io_error!( - std_io::ErrorKind::Unsupported, - "operation not supported on this platform", - ) + std_io::Error::UNSUPPORTED_PLATFORM } pub fn is_interrupted(_code: i32) -> bool { diff --git a/library/std/src/sys_common/mod.rs b/library/std/src/sys_common/mod.rs index 5abf201aa20..cc21560fff5 100644 --- a/library/std/src/sys_common/mod.rs +++ b/library/std/src/sys_common/mod.rs @@ -25,7 +25,6 @@ pub mod fs; pub mod io; pub mod lazy_box; pub mod process; -pub mod thread; pub mod thread_local_dtor; pub mod thread_parking; pub mod wstr; diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys_common/net.rs index 8c9885974b4..6a268633f72 100644 --- a/library/std/src/sys_common/net.rs +++ b/library/std/src/sys_common/net.rs @@ -417,6 +417,10 @@ impl TcpListener { // it allows up to about 37, but other times it doesn't even // accept 32. There may be a global limitation causing this. let backlog = 20; + } else if #[cfg(target_os = "haiku")] { + // Haiku does not support a queue length > 32 + // https://github.com/haiku/haiku/blob/979a0bc487864675517fb2fab28f87dc8bf43041/headers/posix/sys/socket.h#L81 + let backlog = 32; } else { // The default for all other platforms let backlog = 128; diff --git a/library/std/src/sys_common/thread.rs b/library/std/src/sys_common/thread.rs deleted file mode 100644 index 8f5624bbce9..00000000000 --- a/library/std/src/sys_common/thread.rs +++ /dev/null @@ -1,18 +0,0 @@ -use crate::env; -use crate::sync::atomic::{self, Ordering}; -use crate::sys::thread as imp; - -pub fn min_stack() -> usize { - static MIN: atomic::AtomicUsize = atomic::AtomicUsize::new(0); - match MIN.load(Ordering::Relaxed) { - 0 => {} - n => return n - 1, - } - let amt = env::var_os("RUST_MIN_STACK").and_then(|s| s.to_str().and_then(|s| s.parse().ok())); - let amt = amt.unwrap_or(imp::DEFAULT_MIN_STACK_SIZE); - - // 0 is our sentinel value, so ensure that we'll never see 0 after - // initialization has run - MIN.store(amt + 1, Ordering::Relaxed); - amt -} diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs index fbb882e640b..c1b4440e560 100644 --- a/library/std/src/thread/local.rs +++ b/library/std/src/thread/local.rs @@ -53,25 +53,25 @@ use crate::fmt; /// # Examples /// /// ``` -/// use std::cell::RefCell; +/// use std::cell::Cell; /// use std::thread; /// -/// thread_local!(static FOO: RefCell<u32> = RefCell::new(1)); +/// thread_local!(static FOO: Cell<u32> = Cell::new(1)); /// -/// FOO.with_borrow(|v| assert_eq!(*v, 1)); -/// FOO.with_borrow_mut(|v| *v = 2); +/// assert_eq!(FOO.get(), 1); +/// FOO.set(2); /// /// // each thread starts out with the initial value of 1 /// let t = thread::spawn(move|| { -/// FOO.with_borrow(|v| assert_eq!(*v, 1)); -/// FOO.with_borrow_mut(|v| *v = 3); +/// assert_eq!(FOO.get(), 1); +/// FOO.set(3); /// }); /// /// // wait for the thread to complete and bail out on panic /// t.join().unwrap(); /// /// // we retain our original value of 2 despite the child thread -/// FOO.with_borrow(|v| assert_eq!(*v, 2)); +/// assert_eq!(FOO.get(), 2); /// ``` /// /// # Platform-specific behavior @@ -141,15 +141,16 @@ impl<T: 'static> fmt::Debug for LocalKey<T> { /// Publicity and attributes for each static are allowed. Example: /// /// ``` -/// use std::cell::RefCell; +/// use std::cell::{Cell, RefCell}; +/// /// thread_local! { -/// pub static FOO: RefCell<u32> = RefCell::new(1); +/// pub static FOO: Cell<u32> = Cell::new(1); /// -/// static BAR: RefCell<f32> = RefCell::new(1.0); +/// static BAR: RefCell<Vec<f32>> = RefCell::new(vec![1.0, 2.0]); /// } /// -/// FOO.with_borrow(|v| assert_eq!(*v, 1)); -/// BAR.with_borrow(|v| assert_eq!(*v, 1.0)); +/// assert_eq!(FOO.get(), 1); +/// BAR.with_borrow(|v| assert_eq!(v[1], 2.0)); /// ``` /// /// Note that only shared references (`&T`) to the inner data may be obtained, so a @@ -164,12 +165,13 @@ impl<T: 'static> fmt::Debug for LocalKey<T> { /// track any additional state. /// /// ``` -/// use std::cell::Cell; +/// use std::cell::RefCell; +/// /// thread_local! { -/// pub static FOO: Cell<u32> = const { Cell::new(1) }; +/// pub static FOO: RefCell<Vec<u32>> = const { RefCell::new(Vec::new()) }; /// } /// -/// assert_eq!(FOO.get(), 1); +/// FOO.with_borrow(|v| assert_eq!(v.len(), 0)); /// ``` /// /// See [`LocalKey` documentation][`std::thread::LocalKey`] for more @@ -279,10 +281,9 @@ impl<T: 'static> LocalKey<T> { where F: FnOnce(&T) -> R, { - unsafe { - let thread_local = (self.inner)(None).ok_or(AccessError)?; - Ok(f(thread_local)) - } + // SAFETY: `inner` is safe to call within the lifetime of the thread + let thread_local = unsafe { (self.inner)(None).ok_or(AccessError)? }; + Ok(f(thread_local)) } /// Acquires a reference to the value in this TLS key, initializing it with @@ -301,14 +302,17 @@ impl<T: 'static> LocalKey<T> { where F: FnOnce(Option<T>, &T) -> R, { - unsafe { - let mut init = Some(init); - let reference = (self.inner)(Some(&mut init)).expect( + let mut init = Some(init); + + // SAFETY: `inner` is safe to call within the lifetime of the thread + let reference = unsafe { + (self.inner)(Some(&mut init)).expect( "cannot access a Thread Local Storage value \ during or after destruction", - ); - f(init, reference) - } + ) + }; + + f(init, reference) } } @@ -377,7 +381,7 @@ impl<T: 'static> LocalKey<Cell<T>> { where T: Copy, { - self.with(|cell| cell.get()) + self.with(Cell::get) } /// Takes the contained value, leaving `Default::default()` in its place. @@ -407,7 +411,7 @@ impl<T: 'static> LocalKey<Cell<T>> { where T: Default, { - self.with(|cell| cell.take()) + self.with(Cell::take) } /// Replaces the contained value, returning the old value. @@ -578,7 +582,7 @@ impl<T: 'static> LocalKey<RefCell<T>> { where T: Default, { - self.with(|cell| cell.take()) + self.with(RefCell::take) } /// Replaces the contained value, returning the old value. diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 99ca770aacf..604eb05040b 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -160,6 +160,7 @@ mod tests; use crate::any::Any; use crate::cell::{OnceCell, UnsafeCell}; +use crate::env; use crate::ffi::{CStr, CString}; use crate::fmt; use crate::io; @@ -171,9 +172,9 @@ use crate::panicking; use crate::pin::Pin; use crate::ptr::addr_of_mut; use crate::str; +use crate::sync::atomic::{AtomicUsize, Ordering}; use crate::sync::Arc; use crate::sys::thread as imp; -use crate::sys_common::thread; use crate::sys_common::thread_parking::Parker; use crate::sys_common::{AsInner, IntoInner}; use crate::time::{Duration, Instant}; @@ -468,7 +469,23 @@ impl Builder { { let Builder { name, stack_size } = self; - let stack_size = stack_size.unwrap_or_else(thread::min_stack); + let stack_size = stack_size.unwrap_or_else(|| { + static MIN: AtomicUsize = AtomicUsize::new(0); + + match MIN.load(Ordering::Relaxed) { + 0 => {} + n => return n - 1, + } + + let amt = env::var_os("RUST_MIN_STACK") + .and_then(|s| s.to_str().and_then(|s| s.parse().ok())) + .unwrap_or(imp::DEFAULT_MIN_STACK_SIZE); + + // 0 is our sentinel value, so ensure that we'll never see 0 after + // initialization has run + MIN.store(amt + 1, Ordering::Relaxed); + amt + }); let my_thread = name.map_or_else(Thread::new_unnamed, |name| unsafe { Thread::new( @@ -1193,17 +1210,17 @@ impl ThreadId { cfg_if::cfg_if! { if #[cfg(target_has_atomic = "64")] { - use crate::sync::atomic::{AtomicU64, Ordering::Relaxed}; + use crate::sync::atomic::AtomicU64; static COUNTER: AtomicU64 = AtomicU64::new(0); - let mut last = COUNTER.load(Relaxed); + let mut last = COUNTER.load(Ordering::Relaxed); loop { let Some(id) = last.checked_add(1) else { exhausted(); }; - match COUNTER.compare_exchange_weak(last, id, Relaxed, Relaxed) { + match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) { Ok(_) => return ThreadId(NonZero::new(id).unwrap()), Err(id) => last = id, } diff --git a/library/std/src/thread/scoped.rs b/library/std/src/thread/scoped.rs index 7b11e7c17b1..e2e22e5194f 100644 --- a/library/std/src/thread/scoped.rs +++ b/library/std/src/thread/scoped.rs @@ -47,10 +47,16 @@ impl ScopeData { // chance it overflows to 0, which would result in unsoundness. if self.num_running_threads.fetch_add(1, Ordering::Relaxed) > usize::MAX / 2 { // This can only reasonably happen by mem::forget()'ing a lot of ScopedJoinHandles. - self.decrement_num_running_threads(false); - panic!("too many running threads in thread scope"); + self.overflow(); } } + + #[cold] + fn overflow(&self) { + self.decrement_num_running_threads(false); + panic!("too many running threads in thread scope"); + } + pub(super) fn decrement_num_running_threads(&self, panic: bool) { if panic { self.a_thread_panicked.store(true, Ordering::Relaxed); diff --git a/library/stdarch b/library/stdarch -Subproject 967e7afd87cbea3232581a4a55031134ab88f59 +Subproject 7df81ba8c3e2d02c2ace0c5a6f4f32d800c09e5 diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py index 818a7daadca..768aac912ce 100755 --- a/src/bootstrap/configure.py +++ b/src/bootstrap/configure.py @@ -152,9 +152,9 @@ v("default-linker", "rust.default-linker", "the default linker") # (others are conditionally saved). o("manage-submodules", "build.submodules", "let the build manage the git submodules") o("full-bootstrap", "build.full-bootstrap", "build three compilers instead of two (not recommended except for testing reproducible builds)") -o("bootstrap-cache-path", "build.bootstrap-cache-path", "use provided path for the bootstrap cache") o("extended", "build.extended", "build an extended rust tool set") +v("bootstrap-cache-path", None, "use provided path for the bootstrap cache") v("tools", None, "List of extended tools will be installed") v("codegen-backends", None, "List of codegen backends to build") v("build", "build.build", "GNUs ./configure syntax LLVM build triple") @@ -359,6 +359,8 @@ def apply_args(known_args, option_checking, config): set('target.{}.llvm-filecheck'.format(build_triple), value, config) elif option.name == 'tools': set('build.tools', value.split(','), config) + elif option.name == 'bootstrap-cache-path': + set('build.bootstrap-cache-path', value, config) elif option.name == 'codegen-backends': set('rust.codegen-backends', value.split(','), config) elif option.name == 'host': diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index 55180a82885..37d91b14ca1 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -296,7 +296,7 @@ impl Step for Rustc { cargo_subcommand(builder.kind), ); - rustc_cargo(builder, &mut cargo, target, compiler.stage); + rustc_cargo(builder, &mut cargo, target, &compiler); // For ./x.py clippy, don't run with --all-targets because // linting tests and benchmarks can produce very noisy results diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index e03997181ee..9420e40d6c2 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -945,55 +945,10 @@ impl Step for Rustc { "build", ); - rustc_cargo(builder, &mut cargo, target, compiler.stage); + rustc_cargo(builder, &mut cargo, target, &compiler); - if builder.config.rust_profile_use.is_some() - && builder.config.rust_profile_generate.is_some() - { - panic!("Cannot use and generate PGO profiles at the same time"); - } - - // With LLD, we can use ICF (identical code folding) to reduce the executable size - // of librustc_driver/rustc and to improve i-cache utilization. - // - // -Wl,[link options] doesn't work on MSVC. However, /OPT:ICF (technically /OPT:REF,ICF) - // is already on by default in MSVC optimized builds, which is interpreted as --icf=all: - // https://github.com/llvm/llvm-project/blob/3329cec2f79185bafd678f310fafadba2a8c76d2/lld/COFF/Driver.cpp#L1746 - // https://github.com/rust-lang/rust/blob/f22819bcce4abaff7d1246a56eec493418f9f4ee/compiler/rustc_codegen_ssa/src/back/linker.rs#L827 - if builder.config.lld_mode.is_used() && !compiler.host.is_msvc() { - cargo.rustflag("-Clink-args=-Wl,--icf=all"); - } - - let is_collecting = if let Some(path) = &builder.config.rust_profile_generate { - if compiler.stage == 1 { - cargo.rustflag(&format!("-Cprofile-generate={path}")); - // Apparently necessary to avoid overflowing the counters during - // a Cargo build profile - cargo.rustflag("-Cllvm-args=-vp-counters-per-site=4"); - true - } else { - false - } - } else if let Some(path) = &builder.config.rust_profile_use { - if compiler.stage == 1 { - cargo.rustflag(&format!("-Cprofile-use={path}")); - if builder.is_verbose() { - cargo.rustflag("-Cllvm-args=-pgo-warn-missing-function"); - } - true - } else { - false - } - } else { - false - }; - if is_collecting { - // Ensure paths to Rust sources are relative, not absolute. - cargo.rustflag(&format!( - "-Cllvm-args=-static-func-strip-dirname-prefix={}", - builder.config.src.components().count() - )); - } + // NB: all RUSTFLAGS should be added to `rustc_cargo()` so they will be + // consistently applied by check/doc/test modes too. for krate in &*self.crates { cargo.arg("-p").arg(krate); @@ -1044,7 +999,12 @@ impl Step for Rustc { } } -pub fn rustc_cargo(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetSelection, stage: u32) { +pub fn rustc_cargo( + builder: &Builder<'_>, + cargo: &mut Cargo, + target: TargetSelection, + compiler: &Compiler, +) { cargo .arg("--features") .arg(builder.rustc_features(builder.kind, target)) @@ -1055,7 +1015,7 @@ pub fn rustc_cargo(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetSelec // We currently don't support cross-crate LTO in stage0. This also isn't hugely necessary // and may just be a time sink. - if stage != 0 { + if compiler.stage != 0 { match builder.config.rust_lto { RustcLto::Thin | RustcLto::Fat => { // Since using LTO for optimizing dylibs is currently experimental, @@ -1081,7 +1041,52 @@ pub fn rustc_cargo(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetSelec cargo.rustflag("-Clto=off"); } - rustc_cargo_env(builder, cargo, target, stage); + // With LLD, we can use ICF (identical code folding) to reduce the executable size + // of librustc_driver/rustc and to improve i-cache utilization. + // + // -Wl,[link options] doesn't work on MSVC. However, /OPT:ICF (technically /OPT:REF,ICF) + // is already on by default in MSVC optimized builds, which is interpreted as --icf=all: + // https://github.com/llvm/llvm-project/blob/3329cec2f79185bafd678f310fafadba2a8c76d2/lld/COFF/Driver.cpp#L1746 + // https://github.com/rust-lang/rust/blob/f22819bcce4abaff7d1246a56eec493418f9f4ee/compiler/rustc_codegen_ssa/src/back/linker.rs#L827 + if builder.config.lld_mode.is_used() && !compiler.host.is_msvc() { + cargo.rustflag("-Clink-args=-Wl,--icf=all"); + } + + if builder.config.rust_profile_use.is_some() && builder.config.rust_profile_generate.is_some() { + panic!("Cannot use and generate PGO profiles at the same time"); + } + let is_collecting = if let Some(path) = &builder.config.rust_profile_generate { + if compiler.stage == 1 { + cargo.rustflag(&format!("-Cprofile-generate={path}")); + // Apparently necessary to avoid overflowing the counters during + // a Cargo build profile + cargo.rustflag("-Cllvm-args=-vp-counters-per-site=4"); + true + } else { + false + } + } else if let Some(path) = &builder.config.rust_profile_use { + if compiler.stage == 1 { + cargo.rustflag(&format!("-Cprofile-use={path}")); + if builder.is_verbose() { + cargo.rustflag("-Cllvm-args=-pgo-warn-missing-function"); + } + true + } else { + false + } + } else { + false + }; + if is_collecting { + // Ensure paths to Rust sources are relative, not absolute. + cargo.rustflag(&format!( + "-Cllvm-args=-static-func-strip-dirname-prefix={}", + builder.config.src.components().count() + )); + } + + rustc_cargo_env(builder, cargo, target, compiler.stage); } pub fn rustc_cargo_env( diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 51b5cdc0565..a22cbeacf01 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -804,7 +804,7 @@ impl Step for Rustc { // see https://github.com/rust-lang/rust/pull/122066#issuecomment-1983049222 // cargo.rustdocflag("--generate-link-to-definition"); - compile::rustc_cargo(builder, &mut cargo, target, compiler.stage); + compile::rustc_cargo(builder, &mut cargo, target, &compiler); cargo.arg("-Zunstable-options"); cargo.arg("-Zskip-rustdoc-fingerprint"); diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 1e68f8d276a..09763e6bf01 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -2713,7 +2713,7 @@ impl Step for Crate { } } Mode::Rustc => { - compile::rustc_cargo(builder, &mut cargo, target, compiler.stage); + compile::rustc_cargo(builder, &mut cargo, target, &compiler); } _ => panic!("can only test libraries"), }; diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 96dec975250..c84eb8a684f 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -2483,9 +2483,20 @@ impl Config { llvm::is_ci_llvm_available(self, asserts) } }; + match download_ci_llvm { - None => self.channel == "dev" && if_unchanged(), - Some(StringOrBool::Bool(b)) => b, + None => { + (self.channel == "dev" || self.download_rustc_commit.is_some()) && if_unchanged() + } + Some(StringOrBool::Bool(b)) => { + if !b && self.download_rustc_commit.is_some() { + panic!( + "`llvm.download-ci-llvm` cannot be set to `false` if `rust.download-rustc` is set to `true` or `if-unchanged`." + ); + } + + b + } // FIXME: "if-available" is deprecated. Remove this block later (around mid 2024) // to not break builds between the recent-to-old checkouts. Some(StringOrBool::String(s)) if s == "if-available" => { diff --git a/src/ci/docker/host-x86_64/dist-various-1/Dockerfile b/src/ci/docker/host-x86_64/dist-various-1/Dockerfile index 09fbbac466c..5c459e5cd18 100644 --- a/src/ci/docker/host-x86_64/dist-various-1/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-various-1/Dockerfile @@ -99,6 +99,7 @@ ENV TARGETS=$TARGETS,thumbv8m.base-none-eabi ENV TARGETS=$TARGETS,thumbv8m.main-none-eabi ENV TARGETS=$TARGETS,thumbv8m.main-none-eabihf ENV TARGETS=$TARGETS,riscv32i-unknown-none-elf +ENV TARGETS=$TARGETS,riscv32im-unknown-none-elf ENV TARGETS=$TARGETS,riscv32imc-unknown-none-elf ENV TARGETS=$TARGETS,riscv32imac-unknown-none-elf ENV TARGETS=$TARGETS,riscv32imafc-unknown-none-elf @@ -130,6 +131,8 @@ ENV CFLAGS_armv5te_unknown_linux_musleabi="-march=armv5te -marm -mfloat-abi=soft CFLAGS_aarch64_unknown_none=-mstrict-align -march=armv8-a+fp+simd \ CC_riscv32i_unknown_none_elf=riscv32-unknown-elf-gcc \ CFLAGS_riscv32i_unknown_none_elf=-march=rv32i -mabi=ilp32 \ + CC_riscv32im_unknown_none_elf=riscv32-unknown-elf-gcc \ + CFLAGS_riscv32im_unknown_none_elf=-march=rv32im -mabi=ilp32 \ CC_riscv32imc_unknown_none_elf=riscv32-unknown-elf-gcc \ CFLAGS_riscv32imc_unknown_none_elf=-march=rv32imc -mabi=ilp32 \ CC_riscv32imac_unknown_none_elf=riscv32-unknown-elf-gcc \ diff --git a/src/doc/unstable-book/src/compiler-flags/linker-features.md b/src/doc/unstable-book/src/compiler-flags/linker-features.md new file mode 100644 index 00000000000..643fcf7c6d7 --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/linker-features.md @@ -0,0 +1,35 @@ +# `linker-features` + +-------------------- + +The `-Zlinker-features` compiler flag allows enabling or disabling specific features used during +linking, and is intended to be stabilized under the codegen options as `-Clinker-features`. + +These feature flags are a flexible extension mechanism that is complementary to linker flavors, +designed to avoid the combinatorial explosion of having to create a new set of flavors for each +linker feature we'd want to use. + +For example, this design allows: +- default feature sets for principal flavors, or for specific targets. +- flavor-specific features: for example, clang offers automatic cross-linking with `--target`, which + gcc-style compilers don't support. The *flavor* is still a C/C++ compiler, and we don't want to + multiply the number of flavors for this use-case. Instead, we can have a single `+target` feature. +- umbrella features: for example, if clang accumulates more features in the future than just the + `+target` above. That could be modeled as `+clang`. +- niche features for resolving specific issues: for example, on Apple targets the linker flag + implementing the `as-needed` native link modifier (#99424) is only possible on sufficiently recent + linker versions. +- still allows for discovery and automation, for example via feature detection. This can be useful + in exotic environments/build systems. + +The flag accepts a comma-separated list of features, individually enabled (`+features`) or disabled +(`-features`), though currently only one is exposed on the CLI: +- `lld`: to toggle using the lld linker, either the system-installed binary, or the self-contained + `rust-lld` linker. + +As described above, this list is intended to grow in the future. + +One of the most common uses of this flag will be to toggle self-contained linking with `rust-lld` on +and off: `-Clinker-features=+lld -Clink-self-contained=+linker` will use the toolchain's `rust-lld` +as the linker. Inversely, `-Clinker-features=-lld` would opt out of that, if the current target had +self-contained linking enabled by default. diff --git a/src/doc/unstable-book/src/language-features/asm-experimental-arch.md b/src/doc/unstable-book/src/language-features/asm-experimental-arch.md index 59acbc73db4..43e11b6d57d 100644 --- a/src/doc/unstable-book/src/language-features/asm-experimental-arch.md +++ b/src/doc/unstable-book/src/language-features/asm-experimental-arch.md @@ -19,6 +19,7 @@ This feature tracks `asm!` and `global_asm!` support for the following architect - M68k - CSKY - s390x +- Arm64EC ## Register classes @@ -51,6 +52,9 @@ This feature tracks `asm!` and `global_asm!` support for the following architect | CSKY | `freg` | `f[0-31]` | `f` | | s390x | `reg` | `r[0-10]`, `r[12-14]` | `r` | | s390x | `freg` | `f[0-15]` | `f` | +| Arm64EC | `reg` | `x[0-12]`, `x[15-22]`, `x[25-27]`, `x30` | `r` | +| Arm64EC | `vreg` | `v[0-15]` | `w` | +| Arm64EC | `vreg_low16` | `v[0-15]` | `x` | > **Notes**: > - NVPTX doesn't have a fixed register set, so named registers are not supported. @@ -86,6 +90,8 @@ This feature tracks `asm!` and `global_asm!` support for the following architect | CSKY | `freg` | None | `f32`, | | s390x | `reg`, `reg_addr` | None | `i8`, `i16`, `i32`, `i64` | | s390x | `freg` | None | `f32`, `f64` | +| Arm64EC | `reg` | None | `i8`, `i16`, `i32`, `f32`, `i64`, `f64` | +| Arm64EC | `vreg` | None | `i8`, `i16`, `i32`, `f32`, `i64`, `f64`, <br> `i8x8`, `i16x4`, `i32x2`, `i64x1`, `f32x2`, `f64x1`, <br> `i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2` | ## Register aliases @@ -118,6 +124,12 @@ This feature tracks `asm!` and `global_asm!` support for the following architect | CSKY | `r29` | `rtb` | | CSKY | `r30` | `svbr` | | CSKY | `r31` | `tls` | +| Arm64EC | `x[0-30]` | `w[0-30]` | +| Arm64EC | `x29` | `fp` | +| Arm64EC | `x30` | `lr` | +| Arm64EC | `sp` | `wsp` | +| Arm64EC | `xzr` | `wzr` | +| Arm64EC | `v[0-15]` | `b[0-15]`, `h[0-15]`, `s[0-15]`, `d[0-15]`, `q[0-15]` | > **Notes**: > - TI does not mandate a frame pointer for MSP430, but toolchains are allowed @@ -128,8 +140,8 @@ This feature tracks `asm!` and `global_asm!` support for the following architect | Architecture | Unsupported register | Reason | | ------------ | --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | All | `sp`, `r15` (s390x) | The stack pointer must be restored to its original value at the end of an asm code block. | -| All | `fr` (Hexagon), `$fp` (MIPS), `Y` (AVR), `r4` (MSP430), `a6` (M68k), `r11` (s390x) | The frame pointer cannot be used as an input or output. | -| All | `r19` (Hexagon) | This is used internally by LLVM as a "base pointer" for functions with complex stack frames. | +| All | `fr` (Hexagon), `$fp` (MIPS), `Y` (AVR), `r4` (MSP430), `a6` (M68k), `r11` (s390x), `x29` (Arm64EC) | The frame pointer cannot be used as an input or output. | +| All | `r19` (Hexagon), `x19` (Arm64EC) | This is used internally by LLVM as a "base pointer" for functions with complex stack frames. | | MIPS | `$0` or `$zero` | This is a constant zero register which can't be modified. | | MIPS | `$1` or `$at` | Reserved for assembler. | | MIPS | `$26`/`$k0`, `$27`/`$k1` | OS-reserved registers. | @@ -145,6 +157,9 @@ This feature tracks `asm!` and `global_asm!` support for the following architect | CSKY | `r15` | This is the link register. | | CSKY | `r[26-30]` | Reserved by its ABI. | | CSKY | `r31` | This is the TLS register. | +| Arm64EC | `xzr` | This is a constant zero register which can't be modified. | +| Arm64EC | `x18` | This is an OS-reserved register. | +| Arm64EC | `x13`, `x14`, `x23`, `x24`, `x28`, `v[16-31]` | These are AArch64 registers that are not supported for Arm64EC. | ## Template modifiers @@ -165,6 +180,16 @@ This feature tracks `asm!` and `global_asm!` support for the following architect | s390x | `freg` | None | `%f0` | None | | CSKY | `reg` | None | `r0` | None | | CSKY | `freg` | None | `f0` | None | +| Arm64EC | `reg` | None | `x0` | `x` | +| Arm64EC | `reg` | `w` | `w0` | `w` | +| Arm64EC | `reg` | `x` | `x0` | `x` | +| Arm64EC | `vreg` | None | `v0` | None | +| Arm64EC | `vreg` | `v` | `v0` | None | +| Arm64EC | `vreg` | `b` | `b0` | `b` | +| Arm64EC | `vreg` | `h` | `h0` | `h` | +| Arm64EC | `vreg` | `s` | `s0` | `s` | +| Arm64EC | `vreg` | `d` | `d0` | `d` | +| Arm64EC | `vreg` | `q` | `q0` | `q` | # Flags covered by `preserves_flags` @@ -177,3 +202,6 @@ These flags registers must be restored upon exiting the asm block if the `preser - The condition code register `ccr`. - s390x - The condition code register `cc`. +- Arm64EC + - Condition flags (`NZCV` register). + - Floating-point status (`FPSR` register). diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 6f86c6450d9..4d506edc47b 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -63,8 +63,6 @@ pub(crate) fn try_inline( let import_def_id = attrs.and_then(|(_, def_id)| def_id); - let (attrs, cfg) = merge_attrs(cx, load_attrs(cx, did), attrs); - let kind = match res { Res::Def(DefKind::Trait, did) => { record_extern_fqn(cx, did, ItemType::Trait); @@ -134,7 +132,11 @@ pub(crate) fn try_inline( cx.with_param_env(did, |cx| clean::ConstantItem(build_const(cx, did))) } Res::Def(DefKind::Macro(kind), did) => { - let mac = build_macro(cx, did, name, import_def_id, kind, attrs.is_doc_hidden()); + let is_doc_hidden = cx.tcx.is_doc_hidden(did) + || attrs_without_docs + .map(|(attrs, _)| attrs) + .is_some_and(|attrs| utils::attrs_have_doc_flag(attrs.iter(), sym::hidden)); + let mac = build_macro(cx, did, name, import_def_id, kind, is_doc_hidden); let type_kind = match kind { MacroKind::Bang => ItemType::Macro, @@ -148,8 +150,14 @@ pub(crate) fn try_inline( }; cx.inlined.insert(did.into()); - let mut item = - clean::Item::from_def_id_and_attrs_and_parts(did, Some(name), kind, Box::new(attrs), cfg); + let mut item = crate::clean::generate_item_with_correct_attrs( + cx, + kind, + did, + name, + import_def_id.and_then(|def_id| def_id.as_local()), + None, + ); // The visibility needs to reflect the one from the reexport and not from the "source" DefId. item.inline_stmt_id = import_def_id; ret.push(item); @@ -179,6 +187,7 @@ pub(crate) fn try_inline_glob( .iter() .filter(|child| !child.reexport_chain.is_empty()) .filter_map(|child| child.res.opt_def_id()) + .filter(|def_id| !cx.tcx.is_doc_hidden(def_id)) .collect(); let attrs = cx.tcx.hir().attrs(import.hir_id()); let mut items = build_module_items( diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 12f45fe4979..925d41e67f8 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -3007,22 +3007,22 @@ fn clean_use_statement_inner<'tcx>( // were specifically asked for it denied = true; } - if !denied { - if let Some(mut items) = inline::try_inline( + if !denied + && let Some(mut items) = inline::try_inline( cx, path.res, name, Some((attrs, Some(import_def_id))), &mut Default::default(), - ) { - items.push(Item::from_def_id_and_parts( - import_def_id, - None, - ImportItem(Import::new_simple(name, resolve_use_source(cx, path), false)), - cx, - )); - return items; - } + ) + { + items.push(Item::from_def_id_and_parts( + import_def_id, + None, + ImportItem(Import::new_simple(name, resolve_use_source(cx, path), false)), + cx, + )); + return items; } Import::new_simple(name, resolve_use_source(cx, path), true) }; diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index dc62fbb5edb..aa923cc6117 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -580,7 +580,14 @@ pub(crate) fn find_nearest_parent_module(tcx: TyCtxt<'_>, def_id: DefId) -> Opti /// This function exists because it runs on `hir::Attributes` whereas the other is a /// `clean::Attributes` method. pub(crate) fn has_doc_flag(tcx: TyCtxt<'_>, did: DefId, flag: Symbol) -> bool { - tcx.get_attrs(did, sym::doc) + attrs_have_doc_flag(tcx.get_attrs(did, sym::doc), flag) +} + +pub(crate) fn attrs_have_doc_flag<'a>( + mut attrs: impl Iterator<Item = &'a ast::Attribute>, + flag: Symbol, +) -> bool { + attrs .any(|attr| attr.meta_item_list().is_some_and(|l| rustc_attr::list_contains_name(&l, flag))) } diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index 9802097ea29..11fc99eb511 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -203,10 +203,10 @@ impl Cache { impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> { if item.item_id.is_local() { - let is_stripped = matches!(*item.kind, clean::ItemKind::StrippedItem(..)); debug!( - "folding {} (stripped: {is_stripped:?}) \"{:?}\", id {:?}", + "folding {} (stripped: {:?}) \"{:?}\", id {:?}", item.type_(), + item.is_stripped(), item.name, item.item_id ); @@ -246,13 +246,11 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { // trait. if let clean::TraitItem(ref t) = *item.kind { self.cache.traits.entry(item.item_id.expect_def_id()).or_insert_with(|| (**t).clone()); - } - - // Collect all the implementors of traits. - if let clean::ImplItem(ref i) = *item.kind + } else if let clean::ImplItem(ref i) = *item.kind && let Some(trait_) = &i.trait_ && !i.kind.is_blanket() { + // Collect all the implementors of traits. self.cache .implementors .entry(trait_.def_id()) diff --git a/src/librustdoc/html/templates/page.html b/src/librustdoc/html/templates/page.html index 0941f758de5..1dc9041658e 100644 --- a/src/librustdoc/html/templates/page.html +++ b/src/librustdoc/html/templates/page.html @@ -7,8 +7,7 @@ <meta name="description" content="{{page.description}}"> {# #} <title>{{page.title}}</title> {# #} <script>if(window.location.protocol!=="file:") {# Hack to skip preloading fonts locally - see #98769 #} - for(f of "{{files.source_serif_4_regular}},{{files.fira_sans_regular}},{{files.fira_sans_medium}},{{files.source_code_pro_regular}},{{files.source_code_pro_semibold}}".split(",")) {# #} - document.write(`<link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}${f}">`) {# #} + document.head.insertAdjacentHTML("beforeend","{{files.source_serif_4_regular}},{{files.fira_sans_regular}},{{files.fira_sans_medium}},{{files.source_code_pro_regular}},{{files.source_code_pro_semibold}}".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}${f}">`).join("")) {# #} </script> {# #} <link rel="stylesheet" {#+ #} href="{{static_root_path|safe}}{{files.normalize_css}}"> {# #} diff --git a/src/tools/cargo b/src/tools/cargo -Subproject 28e7b2bc0a812f90126be30f48a00a4ada990ea +Subproject 48eca1b164695022295ce466b64b44e4e0228b0 diff --git a/src/tools/clippy/clippy_lints/src/large_include_file.rs b/src/tools/clippy/clippy_lints/src/large_include_file.rs index 0599afca09f..790bed580fd 100644 --- a/src/tools/clippy/clippy_lints/src/large_include_file.rs +++ b/src/tools/clippy/clippy_lints/src/large_include_file.rs @@ -71,7 +71,7 @@ impl LateLintPass<'_> for LargeIncludeFile { span_lint_and_note( cx, LARGE_INCLUDE_FILE, - expr.span, + expr.span.source_callsite(), "attempted to include a large file", None, format!( diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs index b3c729dacdd..3aa979cb11b 100644 --- a/src/tools/clippy/clippy_lints/src/strings.rs +++ b/src/tools/clippy/clippy_lints/src/strings.rs @@ -300,7 +300,7 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { e.span, "calling `as_bytes()` on `include_str!(..)`", "consider using `include_bytes!(..)` instead", - snippet_with_applicability(cx, receiver.span, r#""foo""#, &mut applicability).replacen( + snippet_with_applicability(cx, receiver.span.source_callsite(), r#""foo""#, &mut applicability).replacen( "include_str", "include_bytes", 1, diff --git a/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.stderr b/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.stderr index b45cb11939f..34224065f07 100644 --- a/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.stderr +++ b/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.stderr @@ -7,7 +7,6 @@ LL | const TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt"); = note: the configuration allows a maximum size of 600 bytes = note: `-D clippy::large-include-file` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::large_include_file)]` - = note: this error originates in the macro `include_bytes` (in Nightly builds, run with -Z macro-backtrace for more info) error: attempted to include a large file --> tests/ui-toml/large_include_file/large_include_file.rs:14:35 @@ -16,7 +15,6 @@ LL | const TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: the configuration allows a maximum size of 600 bytes - = note: this error originates in the macro `include_str` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors diff --git a/src/tools/clippy/tests/ui/empty_docs.stderr b/src/tools/clippy/tests/ui/empty_docs.stderr index 28ebea22c5d..5fd7272d7c1 100644 --- a/src/tools/clippy/tests/ui/empty_docs.stderr +++ b/src/tools/clippy/tests/ui/empty_docs.stderr @@ -25,19 +25,20 @@ LL | /// = help: consider removing or filling it error: empty doc comment - --> tests/ui/empty_docs.rs:30:5 + --> tests/ui/empty_docs.rs:30:13 | LL | #[doc = ""] - | ^^^^^^^^^^^ + | ^^ | = help: consider removing or filling it error: empty doc comment - --> tests/ui/empty_docs.rs:33:5 + --> tests/ui/empty_docs.rs:33:13 | -LL | / #[doc = ""] +LL | #[doc = ""] + | _____________^ LL | | #[doc = ""] - | |_______________^ + | |______________^ | = help: consider removing or filling it diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index ec944cb7fb4..e6f4accfd7b 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -790,15 +790,18 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "ignore-thumb", "ignore-thumbv8m.base-none-eabi", "ignore-thumbv8m.main-none-eabi", + "ignore-tvos", "ignore-unix", "ignore-unknown", "ignore-uwp", + "ignore-visionos", "ignore-vxworks", "ignore-wasi", "ignore-wasm", "ignore-wasm32", "ignore-wasm32-bare", "ignore-wasm64", + "ignore-watchos", "ignore-windows", "ignore-windows-gnu", "ignore-x32", @@ -856,6 +859,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "only-cdb", "only-gnu", "only-i686-pc-windows-msvc", + "only-ios", "only-linux", "only-loongarch64", "only-loongarch64-unknown-linux-gnu", @@ -871,10 +875,13 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "only-sparc64", "only-stable", "only-thumb", + "only-tvos", "only-unix", + "only-visionos", "only-wasm32", "only-wasm32-bare", "only-wasm32-wasip1", + "only-watchos", "only-windows", "only-x86", "only-x86_64", @@ -935,16 +942,25 @@ struct HeaderLine<'ln> { pub(crate) struct CheckDirectiveResult<'ln> { is_known_directive: bool, directive_name: &'ln str, + trailing_directive: Option<&'ln str>, } -// Returns `(is_known_directive, directive_name)`. pub(crate) fn check_directive(directive_ln: &str) -> CheckDirectiveResult<'_> { - let directive_name = - directive_ln.split_once([':', ' ']).map(|(pre, _)| pre).unwrap_or(directive_ln); + let (directive_name, post) = directive_ln.split_once([':', ' ']).unwrap_or((directive_ln, "")); + + let trailing = post.trim().split_once(' ').map(|(pre, _)| pre).unwrap_or(post); + let trailing_directive = { + // 1. is the directive name followed by a space? (to exclude `:`) + matches!(directive_ln.get(directive_name.len()..), Some(s) if s.starts_with(" ")) + // 2. is what is after that directive also a directive (ex: "only-x86 only-arm") + && KNOWN_DIRECTIVE_NAMES.contains(&trailing) + } + .then_some(trailing); CheckDirectiveResult { is_known_directive: KNOWN_DIRECTIVE_NAMES.contains(&directive_name), directive_name: directive_ln, + trailing_directive, } } @@ -1014,7 +1030,8 @@ fn iter_header( if testfile.extension().map(|e| e == "rs").unwrap_or(false) { let directive_ln = non_revisioned_directive_line.trim(); - let CheckDirectiveResult { is_known_directive, .. } = check_directive(directive_ln); + let CheckDirectiveResult { is_known_directive, trailing_directive, .. } = + check_directive(directive_ln); if !is_known_directive { *poisoned = true; @@ -1028,6 +1045,21 @@ fn iter_header( return; } + + if let Some(trailing_directive) = &trailing_directive { + *poisoned = true; + + eprintln!( + "error: detected trailing compiletest test directive `{}` in {}:{}\n \ + help: put the trailing directive in it's own line: `//@ {}`", + trailing_directive, + testfile.display(), + line_number, + trailing_directive, + ); + + return; + } } it(HeaderLine { @@ -1051,7 +1083,8 @@ fn iter_header( let rest = rest.trim_start(); - let CheckDirectiveResult { is_known_directive, directive_name } = check_directive(rest); + let CheckDirectiveResult { is_known_directive, directive_name, .. } = + check_directive(rest); if is_known_directive { *poisoned = true; diff --git a/src/tools/compiletest/src/header/tests.rs b/src/tools/compiletest/src/header/tests.rs index 83f0755b5c8..8a37a4d6d31 100644 --- a/src/tools/compiletest/src/header/tests.rs +++ b/src/tools/compiletest/src/header/tests.rs @@ -667,3 +667,24 @@ fn test_non_rs_unknown_directive_not_checked() { ); assert!(!poisoned); } + +#[test] +fn test_trailing_directive() { + let mut poisoned = false; + run_path(&mut poisoned, Path::new("a.rs"), b"//@ only-x86 only-arm"); + assert!(poisoned); +} + +#[test] +fn test_trailing_directive_with_comment() { + let mut poisoned = false; + run_path(&mut poisoned, Path::new("a.rs"), b"//@ only-x86 only-arm with comment"); + assert!(poisoned); +} + +#[test] +fn test_not_trailing_directive() { + let mut poisoned = false; + run_path(&mut poisoned, Path::new("a.rs"), b"//@ revisions: incremental"); + assert!(!poisoned); +} diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 327e34ea36b..5212f1430d8 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -3785,6 +3785,14 @@ impl<'test> TestCx<'test> { debug!(?support_lib_deps); debug!(?support_lib_deps_deps); + let orig_dylib_env_paths = + Vec::from_iter(env::split_paths(&env::var(dylib_env_var()).unwrap())); + + let mut host_dylib_env_paths = Vec::new(); + host_dylib_env_paths.push(cwd.join(&self.config.compile_lib_path)); + host_dylib_env_paths.extend(orig_dylib_env_paths.iter().cloned()); + let host_dylib_env_paths = env::join_paths(host_dylib_env_paths).unwrap(); + let mut cmd = Command::new(&self.config.rustc_path); cmd.arg("-o") .arg(&recipe_bin) @@ -3801,6 +3809,7 @@ impl<'test> TestCx<'test> { .env("RUSTC", cwd.join(&self.config.rustc_path)) .env("TMPDIR", &tmpdir) .env("LD_LIB_PATH_ENVVAR", dylib_env_var()) + .env(dylib_env_var(), &host_dylib_env_paths) .env("HOST_RPATH_DIR", cwd.join(&self.config.compile_lib_path)) .env("TARGET_RPATH_DIR", cwd.join(&self.config.run_lib_path)) .env("LLVM_COMPONENTS", &self.config.llvm_components) @@ -3828,19 +3837,15 @@ impl<'test> TestCx<'test> { // Finally, we need to run the recipe binary to build and run the actual tests. debug!(?recipe_bin); - let mut dylib_env_paths = String::new(); - dylib_env_paths.push_str(&env::var(dylib_env_var()).unwrap()); - dylib_env_paths.push(':'); - dylib_env_paths.push_str(&support_lib_path.parent().unwrap().to_string_lossy()); - dylib_env_paths.push(':'); - dylib_env_paths.push_str( - &stage_std_path.join("rustlib").join(&self.config.host).join("lib").to_string_lossy(), - ); + let mut dylib_env_paths = orig_dylib_env_paths.clone(); + dylib_env_paths.push(support_lib_path.parent().unwrap().to_path_buf()); + dylib_env_paths.push(stage_std_path.join("rustlib").join(&self.config.host).join("lib")); + let dylib_env_paths = env::join_paths(dylib_env_paths).unwrap(); - let mut target_rpath_env_path = String::new(); - target_rpath_env_path.push_str(&tmpdir.to_string_lossy()); - target_rpath_env_path.push(':'); - target_rpath_env_path.push_str(&dylib_env_paths); + let mut target_rpath_env_path = Vec::new(); + target_rpath_env_path.push(&tmpdir); + target_rpath_env_path.extend(&orig_dylib_env_paths); + let target_rpath_env_path = env::join_paths(target_rpath_env_path).unwrap(); let mut cmd = Command::new(&recipe_bin); cmd.current_dir(&self.testpaths.file) diff --git a/src/tools/miri/cargo-miri/src/phases.rs b/src/tools/miri/cargo-miri/src/phases.rs index 3f6c484a057..04f3b2918b5 100644 --- a/src/tools/miri/cargo-miri/src/phases.rs +++ b/src/tools/miri/cargo-miri/src/phases.rs @@ -454,15 +454,10 @@ pub fn phase_rustc(mut args: impl Iterator<Item = String>, phase: RustcPhase) { continue; } // If the REPLACE_LIBRS hack is enabled and we are building a `lib.rs` file, and a - // `lib.miri.rs` file exists, then build that instead. We only consider relative paths - // as cargo uses those for files in the workspace; dependencies from crates.io get - // absolute paths. + // `lib.miri.rs` file exists, then build that instead. if replace_librs { let path = Path::new(&arg); - if path.is_relative() - && path.file_name().is_some_and(|f| f == "lib.rs") - && path.is_file() - { + if path.file_name().is_some_and(|f| f == "lib.rs") && path.is_file() { let miri_rs = Path::new(&arg).with_extension("miri.rs"); if miri_rs.is_file() { if verbose > 0 { diff --git a/src/tools/miri/src/shims/x86/mod.rs b/src/tools/miri/src/shims/x86/mod.rs index 7b7921219e6..2a663d300a7 100644 --- a/src/tools/miri/src/shims/x86/mod.rs +++ b/src/tools/miri/src/shims/x86/mod.rs @@ -468,6 +468,86 @@ fn unary_op_ps<'tcx>( Ok(()) } +enum ShiftOp { + /// Shift left, logically (shift in zeros) -- same as shift left, arithmetically + Left, + /// Shift right, logically (shift in zeros) + RightLogic, + /// Shift right, arithmetically (shift in sign) + RightArith, +} + +/// Shifts each element of `left` by a scalar amount. The shift amount +/// is determined by the lowest 64 bits of `right` (which is a 128-bit vector). +/// +/// For logic shifts, when right is larger than BITS - 1, zero is produced. +/// For arithmetic right-shifts, when right is larger than BITS - 1, the sign +/// bit is copied to remaining bits. +fn shift_simd_by_scalar<'tcx>( + this: &mut crate::MiriInterpCx<'_, 'tcx>, + left: &OpTy<'tcx, Provenance>, + right: &OpTy<'tcx, Provenance>, + which: ShiftOp, + dest: &MPlaceTy<'tcx, Provenance>, +) -> InterpResult<'tcx, ()> { + let (left, left_len) = this.operand_to_simd(left)?; + let (dest, dest_len) = this.mplace_to_simd(dest)?; + + assert_eq!(dest_len, left_len); + // `right` may have a different length, and we only care about its + // lowest 64bit anyway. + + // Get the 64-bit shift operand and convert it to the type expected + // by checked_{shl,shr} (u32). + // It is ok to saturate the value to u32::MAX because any value + // above BITS - 1 will produce the same result. + let shift = u32::try_from(extract_first_u64(this, right)?).unwrap_or(u32::MAX); + + for i in 0..dest_len { + let left = this.read_scalar(&this.project_index(&left, i)?)?; + let dest = this.project_index(&dest, i)?; + + let res = match which { + ShiftOp::Left => { + let left = left.to_uint(dest.layout.size)?; + let res = left.checked_shl(shift).unwrap_or(0); + // `truncate` is needed as left-shift can make the absolute value larger. + Scalar::from_uint(dest.layout.size.truncate(res), dest.layout.size) + } + ShiftOp::RightLogic => { + let left = left.to_uint(dest.layout.size)?; + let res = left.checked_shr(shift).unwrap_or(0); + // No `truncate` needed as right-shift can only make the absolute value smaller. + Scalar::from_uint(res, dest.layout.size) + } + ShiftOp::RightArith => { + let left = left.to_int(dest.layout.size)?; + // On overflow, copy the sign bit to the remaining bits + let res = left.checked_shr(shift).unwrap_or(left >> 127); + // No `truncate` needed as right-shift can only make the absolute value smaller. + Scalar::from_int(res, dest.layout.size) + } + }; + this.write_scalar(res, &dest)?; + } + + Ok(()) +} + +/// Takes a 128-bit vector, transmutes it to `[u64; 2]` and extracts +/// the first value. +fn extract_first_u64<'tcx>( + this: &crate::MiriInterpCx<'_, 'tcx>, + op: &OpTy<'tcx, Provenance>, +) -> InterpResult<'tcx, u64> { + // Transmute vector to `[u64; 2]` + let array_layout = this.layout_of(Ty::new_array(this.tcx.tcx, this.tcx.types.u64, 2))?; + let op = op.transmute(array_layout, this)?; + + // Get the first u64 from the array + this.read_scalar(&this.project_index(&op, 0)?)?.to_u64() +} + // Rounds the first element of `right` according to `rounding` // and copies the remaining elements from `left`. fn round_first<'tcx, F: rustc_apfloat::Float>( diff --git a/src/tools/miri/src/shims/x86/sse2.rs b/src/tools/miri/src/shims/x86/sse2.rs index eb2cc9d37c8..9db30d7ddca 100644 --- a/src/tools/miri/src/shims/x86/sse2.rs +++ b/src/tools/miri/src/shims/x86/sse2.rs @@ -1,10 +1,11 @@ use rustc_apfloat::ieee::Double; -use rustc_middle::ty::layout::LayoutOf as _; -use rustc_middle::ty::Ty; use rustc_span::Symbol; use rustc_target::spec::abi::Abi; -use super::{bin_op_simd_float_all, bin_op_simd_float_first, convert_float_to_int, FloatBinOp}; +use super::{ + bin_op_simd_float_all, bin_op_simd_float_first, convert_float_to_int, shift_simd_by_scalar, + FloatBinOp, ShiftOp, +}; use crate::*; use shims::foreign_items::EmulateForeignItemResult; @@ -109,156 +110,27 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: this.write_scalar(Scalar::from_u64(res.into()), &dest)?; } } - // Used to implement the _mm_{sll,srl,sra}_epi16 functions. - // Shifts 16-bit packed integers in left by the amount in right. - // Both operands are vectors of 16-bit integers. However, right is - // interpreted as a single 64-bit integer (remaining bits are ignored). - // For logic shifts, when right is larger than 15, zero is produced. - // For arithmetic shifts, when right is larger than 15, the sign bit + // Used to implement the _mm_{sll,srl,sra}_epi{16,32,64} functions + // (except _mm_sra_epi64, which is not available in SSE2). + // Shifts N-bit packed integers in left by the amount in right. + // Both operands are 128-bit vectors. However, right is interpreted as + // a single 64-bit integer (remaining bits are ignored). + // For logic shifts, when right is larger than N - 1, zero is produced. + // For arithmetic shifts, when right is larger than N - 1, the sign bit // is copied to remaining bits. - "psll.w" | "psrl.w" | "psra.w" => { + "psll.w" | "psrl.w" | "psra.w" | "psll.d" | "psrl.d" | "psra.d" | "psll.q" + | "psrl.q" => { let [left, right] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let (left, left_len) = this.operand_to_simd(left)?; - let (right, right_len) = this.operand_to_simd(right)?; - let (dest, dest_len) = this.mplace_to_simd(dest)?; - - assert_eq!(dest_len, left_len); - assert_eq!(dest_len, right_len); - - enum ShiftOp { - Sll, - Srl, - Sra, - } let which = match unprefixed_name { - "psll.w" => ShiftOp::Sll, - "psrl.w" => ShiftOp::Srl, - "psra.w" => ShiftOp::Sra, + "psll.w" | "psll.d" | "psll.q" => ShiftOp::Left, + "psrl.w" | "psrl.d" | "psrl.q" => ShiftOp::RightLogic, + "psra.w" | "psra.d" => ShiftOp::RightArith, _ => unreachable!(), }; - // Get the 64-bit shift operand and convert it to the type expected - // by checked_{shl,shr} (u32). - // It is ok to saturate the value to u32::MAX because any value - // above 15 will produce the same result. - let shift = extract_first_u64(this, &right)?.try_into().unwrap_or(u32::MAX); - - for i in 0..dest_len { - let left = this.read_scalar(&this.project_index(&left, i)?)?.to_u16()?; - let dest = this.project_index(&dest, i)?; - - let res = match which { - ShiftOp::Sll => left.checked_shl(shift).unwrap_or(0), - ShiftOp::Srl => left.checked_shr(shift).unwrap_or(0), - #[allow(clippy::cast_possible_wrap, clippy::cast_sign_loss)] - ShiftOp::Sra => { - // Convert u16 to i16 to use arithmetic shift - let left = left as i16; - // Copy the sign bit to the remaining bits - left.checked_shr(shift).unwrap_or(left >> 15) as u16 - } - }; - - this.write_scalar(Scalar::from_u16(res), &dest)?; - } - } - // Used to implement the _mm_{sll,srl,sra}_epi32 functions. - // 32-bit equivalent to the shift functions above. - "psll.d" | "psrl.d" | "psra.d" => { - let [left, right] = - this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - - let (left, left_len) = this.operand_to_simd(left)?; - let (right, right_len) = this.operand_to_simd(right)?; - let (dest, dest_len) = this.mplace_to_simd(dest)?; - - assert_eq!(dest_len, left_len); - assert_eq!(dest_len, right_len); - - enum ShiftOp { - Sll, - Srl, - Sra, - } - let which = match unprefixed_name { - "psll.d" => ShiftOp::Sll, - "psrl.d" => ShiftOp::Srl, - "psra.d" => ShiftOp::Sra, - _ => unreachable!(), - }; - - // Get the 64-bit shift operand and convert it to the type expected - // by checked_{shl,shr} (u32). - // It is ok to saturate the value to u32::MAX because any value - // above 31 will produce the same result. - let shift = extract_first_u64(this, &right)?.try_into().unwrap_or(u32::MAX); - - for i in 0..dest_len { - let left = this.read_scalar(&this.project_index(&left, i)?)?.to_u32()?; - let dest = this.project_index(&dest, i)?; - - let res = match which { - ShiftOp::Sll => left.checked_shl(shift).unwrap_or(0), - ShiftOp::Srl => left.checked_shr(shift).unwrap_or(0), - #[allow(clippy::cast_possible_wrap, clippy::cast_sign_loss)] - ShiftOp::Sra => { - // Convert u32 to i32 to use arithmetic shift - let left = left as i32; - // Copy the sign bit to the remaining bits - left.checked_shr(shift).unwrap_or(left >> 31) as u32 - } - }; - - this.write_scalar(Scalar::from_u32(res), &dest)?; - } - } - // Used to implement the _mm_{sll,srl}_epi64 functions. - // 64-bit equivalent to the shift functions above, except _mm_sra_epi64, - // which is not available in SSE2. - "psll.q" | "psrl.q" => { - let [left, right] = - this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - - let (left, left_len) = this.operand_to_simd(left)?; - let (right, right_len) = this.operand_to_simd(right)?; - let (dest, dest_len) = this.mplace_to_simd(dest)?; - - assert_eq!(dest_len, left_len); - assert_eq!(dest_len, right_len); - - enum ShiftOp { - Sll, - Srl, - } - let which = match unprefixed_name { - "psll.q" => ShiftOp::Sll, - "psrl.q" => ShiftOp::Srl, - _ => unreachable!(), - }; - - // Get the 64-bit shift operand and convert it to the type expected - // by checked_{shl,shr} (u32). - // It is ok to saturate the value to u32::MAX because any value - // above 63 will produce the same result. - let shift = this - .read_scalar(&this.project_index(&right, 0)?)? - .to_u64()? - .try_into() - .unwrap_or(u32::MAX); - - for i in 0..dest_len { - let left = this.read_scalar(&this.project_index(&left, i)?)?.to_u64()?; - let dest = this.project_index(&dest, i)?; - - let res = match which { - ShiftOp::Sll => left.checked_shl(shift).unwrap_or(0), - ShiftOp::Srl => left.checked_shr(shift).unwrap_or(0), - }; - - this.write_scalar(Scalar::from_u64(res), &dest)?; - } + shift_simd_by_scalar(this, left, right, which, dest)?; } // Used to implement the _mm_cvtps_epi32, _mm_cvttps_epi32, _mm_cvtpd_epi32 // and _mm_cvttpd_epi32 functions. @@ -585,17 +457,3 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: Ok(EmulateForeignItemResult::NeedsJumping) } } - -/// Takes a 128-bit vector, transmutes it to `[u64; 2]` and extracts -/// the first value. -fn extract_first_u64<'tcx>( - this: &crate::MiriInterpCx<'_, 'tcx>, - op: &MPlaceTy<'tcx, Provenance>, -) -> InterpResult<'tcx, u64> { - // Transmute vector to `[u64; 2]` - let u64_array_layout = this.layout_of(Ty::new_array(this.tcx.tcx, this.tcx.types.u64, 2))?; - let op = op.transmute(u64_array_layout, this)?; - - // Get the first u64 from the array - this.read_scalar(&this.project_index(&op, 0)?)?.to_u64() -} diff --git a/src/tools/run-make-support/src/cc.rs b/src/tools/run-make-support/src/cc.rs index 2c9ad4f1700..a2d51902652 100644 --- a/src/tools/run-make-support/src/cc.rs +++ b/src/tools/run-make-support/src/cc.rs @@ -1,6 +1,6 @@ use std::env; use std::path::Path; -use std::process::{Command, Output}; +use std::process::Command; use crate::{bin_name, cygpath_windows, handle_failed_output, is_msvc, is_windows, tmp_dir, uname}; @@ -19,6 +19,8 @@ pub struct Cc { cmd: Command, } +crate::impl_common_helpers!(Cc); + impl Cc { /// Construct a new platform-specific C compiler invocation. /// @@ -43,22 +45,6 @@ impl Cc { self } - /// Add a *platform-and-compiler-specific* argument. Please consult the docs for the various - /// possible C compilers on the various platforms to check which arguments are legal for - /// which compiler. - pub fn arg(&mut self, flag: &str) -> &mut Self { - self.cmd.arg(flag); - self - } - - /// Add multiple *platform-and-compiler-specific* arguments. Please consult the docs for the - /// various possible C compilers on the various platforms to check which arguments are legal - /// for which compiler. - pub fn args(&mut self, args: &[&str]) -> &mut Self { - self.cmd.args(args); - self - } - /// Specify `-o` or `-Fe`/`-Fo` depending on platform/compiler. This assumes that the executable /// is under `$TMPDIR`. pub fn out_exe(&mut self, name: &str) -> &mut Self { @@ -85,25 +71,6 @@ impl Cc { self } - - /// Run the constructed C invocation command and assert that it is successfully run. - #[track_caller] - pub fn run(&mut self) -> Output { - let caller_location = std::panic::Location::caller(); - let caller_line_number = caller_location.line(); - - let output = self.cmd.output().unwrap(); - if !output.status.success() { - handle_failed_output(&format!("{:#?}", self.cmd), output, caller_line_number); - } - output - } - - /// Inspect what the underlying [`Command`] is up to the current construction. - pub fn inspect(&mut self, f: impl FnOnce(&Command)) -> &mut Self { - f(&self.cmd); - self - } } /// `EXTRACFLAGS` diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index e70acf58571..47b46a0a699 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -1,3 +1,8 @@ +//! `run-make-support` is a support library for run-make tests. It provides command wrappers and +//! convenience utility functions to help test writers reduce duplication. The support library +//! notably is built via cargo: this means that if your test wants some non-trivial utility, such +//! as `object` or `wasmparser`, they can be re-exported and be made available through this library. + pub mod cc; pub mod run; pub mod rustc; @@ -82,7 +87,7 @@ pub fn cygpath_windows<P: AsRef<Path>>(path: P) -> String { cygpath.arg(path.as_ref()); let output = cygpath.output().unwrap(); if !output.status.success() { - handle_failed_output(&format!("{:#?}", cygpath), output, caller_line_number); + handle_failed_output(&cygpath, output, caller_line_number); } let s = String::from_utf8(output.stdout).unwrap(); // cygpath -w can attach a newline @@ -98,20 +103,158 @@ pub fn uname() -> String { let mut uname = Command::new("uname"); let output = uname.output().unwrap(); if !output.status.success() { - handle_failed_output(&format!("{:#?}", uname), output, caller_line_number); + handle_failed_output(&uname, output, caller_line_number); } String::from_utf8(output.stdout).unwrap() } -fn handle_failed_output(cmd: &str, output: Output, caller_line_number: u32) -> ! { +fn handle_failed_output(cmd: &Command, output: Output, caller_line_number: u32) -> ! { if output.status.success() { - eprintln!("command incorrectly succeeded at line {caller_line_number}"); + eprintln!("command unexpectedly succeeded at line {caller_line_number}"); } else { eprintln!("command failed at line {caller_line_number}"); } - eprintln!("{cmd}"); + eprintln!("{cmd:?}"); eprintln!("output status: `{}`", output.status); eprintln!("=== STDOUT ===\n{}\n\n", String::from_utf8(output.stdout).unwrap()); eprintln!("=== STDERR ===\n{}\n\n", String::from_utf8(output.stderr).unwrap()); std::process::exit(1) } + +/// Set the runtime library path as needed for running the host rustc/rustdoc/etc. +pub fn set_host_rpath(cmd: &mut Command) { + let ld_lib_path_envvar = env::var("LD_LIB_PATH_ENVVAR").unwrap(); + cmd.env(&ld_lib_path_envvar, { + let mut paths = vec![]; + paths.push(PathBuf::from(env::var("TMPDIR").unwrap())); + paths.push(PathBuf::from(env::var("HOST_RPATH_DIR").unwrap())); + for p in env::split_paths(&env::var(&ld_lib_path_envvar).unwrap()) { + paths.push(p.to_path_buf()); + } + env::join_paths(paths.iter()).unwrap() + }); +} + +/// Implement common helpers for command wrappers. This assumes that the command wrapper is a struct +/// containing a `cmd: Command` field. The provided helpers are: +/// +/// 1. Generic argument acceptors: `arg` and `args` (delegated to [`Command`]). These are intended +/// to be *fallback* argument acceptors, when specific helpers don't make sense. Prefer to add +/// new specific helper methods over relying on these generic argument providers. +/// 2. Environment manipulation methods: `env`, `env_remove` and `env_clear`: these delegate to +/// methods of the same name on [`Command`]. +/// 3. Output and execution: `output`, `run` and `run_fail` are provided. `output` waits for the +/// command to finish running and returns the process's [`Output`]. `run` and `run_fail` are +/// higher-level convenience methods which waits for the command to finish running and assert +/// that the command successfully ran or failed as expected. Prefer `run` and `run_fail` when +/// possible. +/// +/// Example usage: +/// +/// ```ignore (illustrative) +/// struct CommandWrapper { cmd: Command } +/// +/// crate::impl_common_helpers!(CommandWrapper); +/// +/// impl CommandWrapper { +/// // ... additional specific helper methods +/// } +/// ``` +/// +/// [`Command`]: ::std::process::Command +/// [`Output`]: ::std::process::Output +macro_rules! impl_common_helpers { + ($wrapper: ident) => { + impl $wrapper { + /// Specify an environment variable. + pub fn env<K, V>(&mut self, key: K, value: V) -> &mut Self + where + K: AsRef<::std::ffi::OsStr>, + V: AsRef<::std::ffi::OsStr>, + { + self.cmd.env(key, value); + self + } + + /// Remove an environmental variable. + pub fn env_remove<K>(&mut self, key: K) -> &mut Self + where + K: AsRef<::std::ffi::OsStr>, + { + self.cmd.env_remove(key); + self + } + + /// Clear all environmental variables. + pub fn env_var(&mut self) -> &mut Self { + self.cmd.env_clear(); + self + } + + /// Generic command argument provider. Prefer specific helper methods if possible. + /// Note that for some executables, arguments might be platform specific. For C/C++ + /// compilers, arguments might be platform *and* compiler specific. + pub fn arg<S>(&mut self, arg: S) -> &mut Self + where + S: AsRef<::std::ffi::OsStr>, + { + self.cmd.arg(arg); + self + } + + /// Generic command arguments provider. Prefer specific helper methods if possible. + /// Note that for some executables, arguments might be platform specific. For C/C++ + /// compilers, arguments might be platform *and* compiler specific. + pub fn args<S>(&mut self, args: &[S]) -> &mut Self + where + S: AsRef<::std::ffi::OsStr>, + { + self.cmd.args(args); + self + } + + /// Inspect what the underlying [`Command`][::std::process::Command] is up to the + /// current construction. + pub fn inspect<I>(&mut self, inspector: I) -> &mut Self + where + I: FnOnce(&::std::process::Command), + { + inspector(&self.cmd); + self + } + + /// Get the [`Output`][::std::process::Output] of the finished process. + pub fn output(&mut self) -> ::std::process::Output { + self.cmd.output().expect("failed to get output of finished process") + } + + /// Run the constructed command and assert that it is successfully run. + #[track_caller] + pub fn run(&mut self) -> ::std::process::Output { + let caller_location = ::std::panic::Location::caller(); + let caller_line_number = caller_location.line(); + + let output = self.cmd.output().unwrap(); + if !output.status.success() { + handle_failed_output(&self.cmd, output, caller_line_number); + } + output + } + + /// Run the constructed command and assert that it does not successfully run. + #[track_caller] + pub fn run_fail(&mut self) -> ::std::process::Output { + let caller_location = ::std::panic::Location::caller(); + let caller_line_number = caller_location.line(); + + let output = self.cmd.output().unwrap(); + if output.status.success() { + handle_failed_output(&self.cmd, output, caller_line_number); + } + output + } + } + }; +} + +pub(crate) use impl_common_helpers; diff --git a/src/tools/run-make-support/src/run.rs b/src/tools/run-make-support/src/run.rs index e33ea9d6e40..9aad91f1b46 100644 --- a/src/tools/run-make-support/src/run.rs +++ b/src/tools/run-make-support/src/run.rs @@ -45,7 +45,7 @@ pub fn run(name: &str) -> Output { let (cmd, output) = run_common(name); if !output.status.success() { - handle_failed_output(&format!("{:#?}", cmd), output, caller_line_number); + handle_failed_output(&cmd, output, caller_line_number); } output } @@ -58,7 +58,7 @@ pub fn run_fail(name: &str) -> Output { let (cmd, output) = run_common(name); if output.status.success() { - handle_failed_output(&format!("{:#?}", cmd), output, caller_line_number); + handle_failed_output(&cmd, output, caller_line_number); } output } diff --git a/src/tools/run-make-support/src/rustc.rs b/src/tools/run-make-support/src/rustc.rs index 5a5b008a7cb..ebda151b908 100644 --- a/src/tools/run-make-support/src/rustc.rs +++ b/src/tools/run-make-support/src/rustc.rs @@ -1,9 +1,9 @@ use std::env; -use std::ffi::{OsStr, OsString}; +use std::ffi::OsString; use std::path::Path; use std::process::{Command, Output}; -use crate::{handle_failed_output, tmp_dir}; +use crate::{handle_failed_output, set_host_rpath, tmp_dir}; /// Construct a new `rustc` invocation. pub fn rustc() -> Rustc { @@ -21,9 +21,12 @@ pub struct Rustc { cmd: Command, } +crate::impl_common_helpers!(Rustc); + fn setup_common() -> Command { let rustc = env::var("RUSTC").unwrap(); let mut cmd = Command::new(rustc); + set_host_rpath(&mut cmd); cmd.arg("--out-dir").arg(tmp_dir()).arg("-L").arg(tmp_dir()); cmd } @@ -132,12 +135,6 @@ impl Rustc { self } - /// Generic command argument provider. Use `.arg("-Zname")` over `.arg("-Z").arg("arg")`. - pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Self { - self.cmd.arg(arg); - self - } - /// Specify the crate type. pub fn crate_type(&mut self, crate_type: &str) -> &mut Self { self.cmd.arg("--crate-type"); @@ -152,49 +149,6 @@ impl Rustc { self } - /// Generic command arguments provider. Use `.arg("-Zname")` over `.arg("-Z").arg("arg")`. - pub fn args<S: AsRef<OsStr>>(&mut self, args: &[S]) -> &mut Self { - self.cmd.args(args); - self - } - - pub fn env(&mut self, name: impl AsRef<OsStr>, value: impl AsRef<OsStr>) -> &mut Self { - self.cmd.env(name, value); - self - } - - // Command inspection, output and running helper methods - - /// Get the [`Output`][std::process::Output] of the finished `rustc` process. - pub fn output(&mut self) -> Output { - self.cmd.output().unwrap() - } - - /// Run the constructed `rustc` command and assert that it is successfully run. - #[track_caller] - pub fn run(&mut self) -> Output { - let caller_location = std::panic::Location::caller(); - let caller_line_number = caller_location.line(); - - let output = self.cmd.output().unwrap(); - if !output.status.success() { - handle_failed_output(&format!("{:#?}", self.cmd), output, caller_line_number); - } - output - } - - #[track_caller] - pub fn run_fail(&mut self) -> Output { - let caller_location = std::panic::Location::caller(); - let caller_line_number = caller_location.line(); - - let output = self.cmd.output().unwrap(); - if output.status.success() { - handle_failed_output(&format!("{:#?}", self.cmd), output, caller_line_number); - } - output - } - #[track_caller] pub fn run_fail_assert_exit_code(&mut self, code: i32) -> Output { let caller_location = std::panic::Location::caller(); @@ -202,14 +156,8 @@ impl Rustc { let output = self.cmd.output().unwrap(); if output.status.code().unwrap() != code { - handle_failed_output(&format!("{:#?}", self.cmd), output, caller_line_number); + handle_failed_output(&self.cmd, output, caller_line_number); } output } - - /// Inspect what the underlying [`Command`] is up to the current construction. - pub fn inspect(&mut self, f: impl FnOnce(&Command)) -> &mut Self { - f(&self.cmd); - self - } } diff --git a/src/tools/run-make-support/src/rustdoc.rs b/src/tools/run-make-support/src/rustdoc.rs index 1fb4b589d76..1054ac83c10 100644 --- a/src/tools/run-make-support/src/rustdoc.rs +++ b/src/tools/run-make-support/src/rustdoc.rs @@ -1,9 +1,8 @@ use std::env; -use std::ffi::OsStr; use std::path::Path; use std::process::{Command, Output}; -use crate::handle_failed_output; +use crate::{handle_failed_output, set_host_rpath}; /// Construct a plain `rustdoc` invocation with no flags set. pub fn bare_rustdoc() -> Rustdoc { @@ -20,9 +19,13 @@ pub struct Rustdoc { cmd: Command, } +crate::impl_common_helpers!(Rustdoc); + fn setup_common() -> Command { let rustdoc = env::var("RUSTDOC").unwrap(); - Command::new(rustdoc) + let mut cmd = Command::new(rustdoc); + set_host_rpath(&mut cmd); + cmd } impl Rustdoc { @@ -59,25 +62,6 @@ impl Rustdoc { self } - /// Generic command argument provider. Use `.arg("-Zname")` over `.arg("-Z").arg("arg")`. - pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Self { - self.cmd.arg(arg); - self - } - - /// Run the build `rustdoc` command and assert that the run is successful. - #[track_caller] - pub fn run(&mut self) -> Output { - let caller_location = std::panic::Location::caller(); - let caller_line_number = caller_location.line(); - - let output = self.cmd.output().unwrap(); - if !output.status.success() { - handle_failed_output(&format!("{:#?}", self.cmd), output, caller_line_number); - } - output - } - #[track_caller] pub fn run_fail_assert_exit_code(&mut self, code: i32) -> Output { let caller_location = std::panic::Location::caller(); @@ -85,7 +69,7 @@ impl Rustdoc { let output = self.cmd.output().unwrap(); if output.status.code().unwrap() != code { - handle_failed_output(&format!("{:#?}", self.cmd), output, caller_line_number); + handle_failed_output(&self.cmd, output, caller_line_number); } output } diff --git a/src/tools/tidy/Cargo.toml b/src/tools/tidy/Cargo.toml index 8c6b1eb22ec..58302b8e63b 100644 --- a/src/tools/tidy/Cargo.toml +++ b/src/tools/tidy/Cargo.toml @@ -6,7 +6,6 @@ autobins = false [dependencies] cargo_metadata = "0.15" -cargo-platform = "0.1.2" regex = "1" miropt-test-tools = { path = "../miropt-test-tools" } lazy_static = "1" diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index aec2856dbbc..f380513f4ef 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -1,6 +1,6 @@ //! Checks the licenses of third-party dependencies. -use cargo_metadata::{DepKindInfo, Metadata, Package, PackageId}; +use cargo_metadata::{Metadata, Package, PackageId}; use std::collections::HashSet; use std::path::Path; @@ -191,6 +191,7 @@ const PERMITTED_DEPS_LOCATION: &str = concat!(file!(), ":", line!()); /// rustc. Please check with the compiler team before adding an entry. const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ // tidy-alphabetical-start + "addr2line", "adler", "ahash", "aho-corasick", @@ -468,6 +469,7 @@ const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[ "mach", "memchr", "object", + "once_cell", "proc-macro2", "quote", "regalloc2", @@ -668,27 +670,7 @@ fn check_permitted_dependencies( let mut deps = HashSet::new(); for to_check in restricted_dependency_crates { let to_check = pkg_from_name(metadata, to_check); - use cargo_platform::Cfg; - use std::str::FromStr; - // We don't expect the compiler to ever run on wasm32, so strip - // out those dependencies to avoid polluting the permitted list. - deps_of_filtered(metadata, &to_check.id, &mut deps, &|dep_kinds| { - dep_kinds.iter().any(|dep_kind| { - dep_kind - .target - .as_ref() - .map(|target| { - !target.matches( - "wasm32-unknown-unknown", - &[ - Cfg::from_str("target_arch=\"wasm32\"").unwrap(), - Cfg::from_str("target_os=\"unknown\"").unwrap(), - ], - ) - }) - .unwrap_or(true) - }) - }); + deps_of(metadata, &to_check.id, &mut deps); } // Check that the PERMITTED_DEPENDENCIES does not have unused entries. @@ -740,18 +722,13 @@ fn compute_runtime_crates<'a>(metadata: &'a Metadata) -> HashSet<&'a PackageId> let mut result = HashSet::new(); for name in RUNTIME_CRATES { let id = &pkg_from_name(metadata, name).id; - deps_of_filtered(metadata, id, &mut result, &|_| true); + deps_of(metadata, id, &mut result); } result } /// Recursively find all dependencies. -fn deps_of_filtered<'a>( - metadata: &'a Metadata, - pkg_id: &'a PackageId, - result: &mut HashSet<&'a PackageId>, - filter: &dyn Fn(&[DepKindInfo]) -> bool, -) { +fn deps_of<'a>(metadata: &'a Metadata, pkg_id: &'a PackageId, result: &mut HashSet<&'a PackageId>) { if !result.insert(pkg_id) { return; } @@ -764,9 +741,6 @@ fn deps_of_filtered<'a>( .find(|n| &n.id == pkg_id) .unwrap_or_else(|| panic!("could not find `{pkg_id}` in resolve")); for dep in &node.deps { - if !filter(&dep.dep_kinds) { - continue; - } - deps_of_filtered(metadata, &dep.pkg, result, filter); + deps_of(metadata, &dep.pkg, result); } } diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt index e14932ad99d..211dc347b0f 100644 --- a/src/tools/tidy/src/issues.txt +++ b/src/tools/tidy/src/issues.txt @@ -1791,7 +1791,6 @@ ui/issues/issue-2150.rs ui/issues/issue-2151.rs ui/issues/issue-21546.rs ui/issues/issue-21554.rs -ui/issues/issue-21596.rs ui/issues/issue-21600.rs ui/issues/issue-21622.rs ui/issues/issue-21634.rs diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index 78de8c0537d..7136bc4d8f2 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -17,7 +17,7 @@ use std::path::{Path, PathBuf}; const ENTRY_LIMIT: usize = 900; // FIXME: The following limits should be reduced eventually. -const ISSUES_ENTRY_LIMIT: usize = 1722; +const ISSUES_ENTRY_LIMIT: usize = 1720; const ROOT_ENTRY_LIMIT: usize = 859; const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[ diff --git a/tests/assembly/asm/aarch64-types.rs b/tests/assembly/asm/aarch64-types.rs index 1b2bd4b3d81..3e2a4773703 100644 --- a/tests/assembly/asm/aarch64-types.rs +++ b/tests/assembly/asm/aarch64-types.rs @@ -1,8 +1,11 @@ +//@ revisions: aarch64 arm64ec //@ assembly-output: emit-asm -//@ compile-flags: --target aarch64-unknown-linux-gnu -//@ needs-llvm-components: aarch64 +//@ [aarch64] compile-flags: --target aarch64-unknown-linux-gnu +//@ [aarch64] needs-llvm-components: aarch64 +//@ [arm64ec] compile-flags: --target arm64ec-pc-windows-msvc +//@ [arm64ec] needs-llvm-components: aarch64 -#![feature(no_core, lang_items, rustc_attrs, repr_simd)] +#![feature(no_core, lang_items, rustc_attrs, repr_simd, asm_experimental_arch)] #![crate_type = "rlib"] #![no_core] #![allow(asm_sub_register, non_camel_case_types)] @@ -77,7 +80,7 @@ extern "C" { static extern_static: u8; } -// CHECK-LABEL: sym_fn: +// CHECK-LABEL: {{("#)?}}sym_fn{{"?}} // CHECK: //APP // CHECK: bl extern_func // CHECK: //NO_APP @@ -86,7 +89,7 @@ pub unsafe fn sym_fn() { asm!("bl {}", sym extern_func); } -// CHECK-LABEL: sym_static: +// CHECK-LABEL: {{("#)?}}sym_static{{"?}} // CHECK: //APP // CHECK: adr x0, extern_static // CHECK: //NO_APP @@ -96,7 +99,7 @@ pub unsafe fn sym_static() { } // Regression test for #75761 -// CHECK-LABEL: issue_75761: +// CHECK-LABEL: {{("#)?}}issue_75761{{"?}} // CHECK: str {{.*}}x30 // CHECK: //APP // CHECK: //NO_APP @@ -144,421 +147,421 @@ macro_rules! check_reg { }; } -// CHECK-LABEL: reg_i8: +// CHECK-LABEL: {{("#)?}}reg_i8{{"?}} // CHECK: //APP // CHECK: mov x{{[0-9]+}}, x{{[0-9]+}} // CHECK: //NO_APP check!(reg_i8 i8 reg "mov" ""); -// CHECK-LABEL: reg_i16: +// CHECK-LABEL: {{("#)?}}reg_i16{{"?}} // CHECK: //APP // CHECK: mov x{{[0-9]+}}, x{{[0-9]+}} // CHECK: //NO_APP check!(reg_i16 i16 reg "mov" ""); -// CHECK-LABEL: reg_i32: +// CHECK-LABEL: {{("#)?}}reg_i32{{"?}} // CHECK: //APP // CHECK: mov x{{[0-9]+}}, x{{[0-9]+}} // CHECK: //NO_APP check!(reg_i32 i32 reg "mov" ""); -// CHECK-LABEL: reg_f32: +// CHECK-LABEL: {{("#)?}}reg_f32{{"?}} // CHECK: //APP // CHECK: mov x{{[0-9]+}}, x{{[0-9]+}} // CHECK: //NO_APP check!(reg_f32 f32 reg "mov" ""); -// CHECK-LABEL: reg_i64: +// CHECK-LABEL: {{("#)?}}reg_i64{{"?}} // CHECK: //APP // CHECK: mov x{{[0-9]+}}, x{{[0-9]+}} // CHECK: //NO_APP check!(reg_i64 i64 reg "mov" ""); -// CHECK-LABEL: reg_f64: +// CHECK-LABEL: {{("#)?}}reg_f64{{"?}} // CHECK: //APP // CHECK: mov x{{[0-9]+}}, x{{[0-9]+}} // CHECK: //NO_APP check!(reg_f64 f64 reg "mov" ""); -// CHECK-LABEL: reg_ptr: +// CHECK-LABEL: {{("#)?}}reg_ptr{{"?}} // CHECK: //APP // CHECK: mov x{{[0-9]+}}, x{{[0-9]+}} // CHECK: //NO_APP check!(reg_ptr ptr reg "mov" ""); -// CHECK-LABEL: vreg_i8: +// CHECK-LABEL: {{("#)?}}vreg_i8{{"?}} // CHECK: //APP // CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} // CHECK: //NO_APP check!(vreg_i8 i8 vreg "fmov" "s"); -// CHECK-LABEL: vreg_i16: +// CHECK-LABEL: {{("#)?}}vreg_i16{{"?}} // CHECK: //APP // CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} // CHECK: //NO_APP check!(vreg_i16 i16 vreg "fmov" "s"); -// CHECK-LABEL: vreg_i32: +// CHECK-LABEL: {{("#)?}}vreg_i32{{"?}} // CHECK: //APP // CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} // CHECK: //NO_APP check!(vreg_i32 i32 vreg "fmov" "s"); -// CHECK-LABEL: vreg_f32: +// CHECK-LABEL: {{("#)?}}vreg_f32{{"?}} // CHECK: //APP // CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} // CHECK: //NO_APP check!(vreg_f32 f32 vreg "fmov" "s"); -// CHECK-LABEL: vreg_i64: +// CHECK-LABEL: {{("#)?}}vreg_i64{{"?}} // CHECK: //APP // CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} // CHECK: //NO_APP check!(vreg_i64 i64 vreg "fmov" "s"); -// CHECK-LABEL: vreg_f64: +// CHECK-LABEL: {{("#)?}}vreg_f64{{"?}} // CHECK: //APP // CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} // CHECK: //NO_APP check!(vreg_f64 f64 vreg "fmov" "s"); -// CHECK-LABEL: vreg_ptr: +// CHECK-LABEL: {{("#)?}}vreg_ptr{{"?}} // CHECK: //APP // CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} // CHECK: //NO_APP check!(vreg_ptr ptr vreg "fmov" "s"); -// CHECK-LABEL: vreg_i8x8: +// CHECK-LABEL: {{("#)?}}vreg_i8x8{{"?}} // CHECK: //APP // CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} // CHECK: //NO_APP check!(vreg_i8x8 i8x8 vreg "fmov" "s"); -// CHECK-LABEL: vreg_i16x4: +// CHECK-LABEL: {{("#)?}}vreg_i16x4{{"?}} // CHECK: //APP // CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} // CHECK: //NO_APP check!(vreg_i16x4 i16x4 vreg "fmov" "s"); -// CHECK-LABEL: vreg_i32x2: +// CHECK-LABEL: {{("#)?}}vreg_i32x2{{"?}} // CHECK: //APP // CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} // CHECK: //NO_APP check!(vreg_i32x2 i32x2 vreg "fmov" "s"); -// CHECK-LABEL: vreg_i64x1: +// CHECK-LABEL: {{("#)?}}vreg_i64x1{{"?}} // CHECK: //APP // CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} // CHECK: //NO_APP check!(vreg_i64x1 i64x1 vreg "fmov" "s"); -// CHECK-LABEL: vreg_f32x2: +// CHECK-LABEL: {{("#)?}}vreg_f32x2{{"?}} // CHECK: //APP // CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} // CHECK: //NO_APP check!(vreg_f32x2 f32x2 vreg "fmov" "s"); -// CHECK-LABEL: vreg_f64x1: +// CHECK-LABEL: {{("#)?}}vreg_f64x1{{"?}} // CHECK: //APP // CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} // CHECK: //NO_APP check!(vreg_f64x1 f64x1 vreg "fmov" "s"); -// CHECK-LABEL: vreg_i8x16: +// CHECK-LABEL: {{("#)?}}vreg_i8x16{{"?}} // CHECK: //APP // CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} // CHECK: //NO_APP check!(vreg_i8x16 i8x16 vreg "fmov" "s"); -// CHECK-LABEL: vreg_i16x8: +// CHECK-LABEL: {{("#)?}}vreg_i16x8{{"?}} // CHECK: //APP // CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} // CHECK: //NO_APP check!(vreg_i16x8 i16x8 vreg "fmov" "s"); -// CHECK-LABEL: vreg_i32x4: +// CHECK-LABEL: {{("#)?}}vreg_i32x4{{"?}} // CHECK: //APP // CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} // CHECK: //NO_APP check!(vreg_i32x4 i32x4 vreg "fmov" "s"); -// CHECK-LABEL: vreg_i64x2: +// CHECK-LABEL: {{("#)?}}vreg_i64x2{{"?}} // CHECK: //APP // CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} // CHECK: //NO_APP check!(vreg_i64x2 i64x2 vreg "fmov" "s"); -// CHECK-LABEL: vreg_f32x4: +// CHECK-LABEL: {{("#)?}}vreg_f32x4{{"?}} // CHECK: //APP // CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} // CHECK: //NO_APP check!(vreg_f32x4 f32x4 vreg "fmov" "s"); -// CHECK-LABEL: vreg_f64x2: +// CHECK-LABEL: {{("#)?}}vreg_f64x2{{"?}} // CHECK: //APP // CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} // CHECK: //NO_APP check!(vreg_f64x2 f64x2 vreg "fmov" "s"); -// CHECK-LABEL: vreg_low16_i8: +// CHECK-LABEL: {{("#)?}}vreg_low16_i8{{"?}} // CHECK: //APP // CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} // CHECK: //NO_APP check!(vreg_low16_i8 i8 vreg_low16 "fmov" "s"); -// CHECK-LABEL: vreg_low16_i16: +// CHECK-LABEL: {{("#)?}}vreg_low16_i16{{"?}} // CHECK: //APP // CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} // CHECK: //NO_APP check!(vreg_low16_i16 i16 vreg_low16 "fmov" "s"); -// CHECK-LABEL: vreg_low16_f32: +// CHECK-LABEL: {{("#)?}}vreg_low16_f32{{"?}} // CHECK: //APP // CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} // CHECK: //NO_APP check!(vreg_low16_f32 f32 vreg_low16 "fmov" "s"); -// CHECK-LABEL: vreg_low16_i64: +// CHECK-LABEL: {{("#)?}}vreg_low16_i64{{"?}} // CHECK: //APP // CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} // CHECK: //NO_APP check!(vreg_low16_i64 i64 vreg_low16 "fmov" "s"); -// CHECK-LABEL: vreg_low16_f64: +// CHECK-LABEL: {{("#)?}}vreg_low16_f64{{"?}} // CHECK: //APP // CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} // CHECK: //NO_APP check!(vreg_low16_f64 f64 vreg_low16 "fmov" "s"); -// CHECK-LABEL: vreg_low16_ptr: +// CHECK-LABEL: {{("#)?}}vreg_low16_ptr{{"?}} // CHECK: //APP // CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} // CHECK: //NO_APP check!(vreg_low16_ptr ptr vreg_low16 "fmov" "s"); -// CHECK-LABEL: vreg_low16_i8x8: +// CHECK-LABEL: {{("#)?}}vreg_low16_i8x8{{"?}} // CHECK: //APP // CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} // CHECK: //NO_APP check!(vreg_low16_i8x8 i8x8 vreg_low16 "fmov" "s"); -// CHECK-LABEL: vreg_low16_i16x4: +// CHECK-LABEL: {{("#)?}}vreg_low16_i16x4{{"?}} // CHECK: //APP // CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} // CHECK: //NO_APP check!(vreg_low16_i16x4 i16x4 vreg_low16 "fmov" "s"); -// CHECK-LABEL: vreg_low16_i32x2: +// CHECK-LABEL: {{("#)?}}vreg_low16_i32x2{{"?}} // CHECK: //APP // CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} // CHECK: //NO_APP check!(vreg_low16_i32x2 i32x2 vreg_low16 "fmov" "s"); -// CHECK-LABEL: vreg_low16_i64x1: +// CHECK-LABEL: {{("#)?}}vreg_low16_i64x1{{"?}} // CHECK: //APP // CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} // CHECK: //NO_APP check!(vreg_low16_i64x1 i64x1 vreg_low16 "fmov" "s"); -// CHECK-LABEL: vreg_low16_f32x2: +// CHECK-LABEL: {{("#)?}}vreg_low16_f32x2{{"?}} // CHECK: //APP // CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} // CHECK: //NO_APP check!(vreg_low16_f32x2 f32x2 vreg_low16 "fmov" "s"); -// CHECK-LABEL: vreg_low16_f64x1: +// CHECK-LABEL: {{("#)?}}vreg_low16_f64x1{{"?}} // CHECK: //APP // CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} // CHECK: //NO_APP check!(vreg_low16_f64x1 f64x1 vreg_low16 "fmov" "s"); -// CHECK-LABEL: vreg_low16_i8x16: +// CHECK-LABEL: {{("#)?}}vreg_low16_i8x16{{"?}} // CHECK: //APP // CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} // CHECK: //NO_APP check!(vreg_low16_i8x16 i8x16 vreg_low16 "fmov" "s"); -// CHECK-LABEL: vreg_low16_i16x8: +// CHECK-LABEL: {{("#)?}}vreg_low16_i16x8{{"?}} // CHECK: //APP // CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} // CHECK: //NO_APP check!(vreg_low16_i16x8 i16x8 vreg_low16 "fmov" "s"); -// CHECK-LABEL: vreg_low16_i32x4: +// CHECK-LABEL: {{("#)?}}vreg_low16_i32x4{{"?}} // CHECK: //APP // CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} // CHECK: //NO_APP check!(vreg_low16_i32x4 i32x4 vreg_low16 "fmov" "s"); -// CHECK-LABEL: vreg_low16_i64x2: +// CHECK-LABEL: {{("#)?}}vreg_low16_i64x2{{"?}} // CHECK: //APP // CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} // CHECK: //NO_APP check!(vreg_low16_i64x2 i64x2 vreg_low16 "fmov" "s"); -// CHECK-LABEL: vreg_low16_f32x4: +// CHECK-LABEL: {{("#)?}}vreg_low16_f32x4{{"?}} // CHECK: //APP // CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} // CHECK: //NO_APP check!(vreg_low16_f32x4 f32x4 vreg_low16 "fmov" "s"); -// CHECK-LABEL: vreg_low16_f64x2: +// CHECK-LABEL: {{("#)?}}vreg_low16_f64x2{{"?}} // CHECK: //APP // CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} // CHECK: //NO_APP check!(vreg_low16_f64x2 f64x2 vreg_low16 "fmov" "s"); -// CHECK-LABEL: x0_i8: +// CHECK-LABEL: {{("#)?}}x0_i8{{"?}} // CHECK: //APP // CHECK: mov x{{[0-9]+}}, x{{[0-9]+}} // CHECK: //NO_APP check_reg!(x0_i8 i8 "x0" "mov"); -// CHECK-LABEL: x0_i16: +// CHECK-LABEL: {{("#)?}}x0_i16{{"?}} // CHECK: //APP // CHECK: mov x{{[0-9]+}}, x{{[0-9]+}} // CHECK: //NO_APP check_reg!(x0_i16 i16 "x0" "mov"); -// CHECK-LABEL: x0_i32: +// CHECK-LABEL: {{("#)?}}x0_i32{{"?}} // CHECK: //APP // CHECK: mov x{{[0-9]+}}, x{{[0-9]+}} // CHECK: //NO_APP check_reg!(x0_i32 i32 "x0" "mov"); -// CHECK-LABEL: x0_f32: +// CHECK-LABEL: {{("#)?}}x0_f32{{"?}} // CHECK: //APP // CHECK: mov x{{[0-9]+}}, x{{[0-9]+}} // CHECK: //NO_APP check_reg!(x0_f32 f32 "x0" "mov"); -// CHECK-LABEL: x0_i64: +// CHECK-LABEL: {{("#)?}}x0_i64{{"?}} // CHECK: //APP // CHECK: mov x{{[0-9]+}}, x{{[0-9]+}} // CHECK: //NO_APP check_reg!(x0_i64 i64 "x0" "mov"); -// CHECK-LABEL: x0_f64: +// CHECK-LABEL: {{("#)?}}x0_f64{{"?}} // CHECK: //APP // CHECK: mov x{{[0-9]+}}, x{{[0-9]+}} // CHECK: //NO_APP check_reg!(x0_f64 f64 "x0" "mov"); -// CHECK-LABEL: x0_ptr: +// CHECK-LABEL: {{("#)?}}x0_ptr{{"?}} // CHECK: //APP // CHECK: mov x{{[0-9]+}}, x{{[0-9]+}} // CHECK: //NO_APP check_reg!(x0_ptr ptr "x0" "mov"); -// CHECK-LABEL: v0_i8: +// CHECK-LABEL: {{("#)?}}v0_i8{{"?}} // CHECK: //APP // CHECK: fmov s0, s0 // CHECK: //NO_APP check_reg!(v0_i8 i8 "s0" "fmov"); -// CHECK-LABEL: v0_i16: +// CHECK-LABEL: {{("#)?}}v0_i16{{"?}} // CHECK: //APP // CHECK: fmov s0, s0 // CHECK: //NO_APP check_reg!(v0_i16 i16 "s0" "fmov"); -// CHECK-LABEL: v0_i32: +// CHECK-LABEL: {{("#)?}}v0_i32{{"?}} // CHECK: //APP // CHECK: fmov s0, s0 // CHECK: //NO_APP check_reg!(v0_i32 i32 "s0" "fmov"); -// CHECK-LABEL: v0_f32: +// CHECK-LABEL: {{("#)?}}v0_f32{{"?}} // CHECK: //APP // CHECK: fmov s0, s0 // CHECK: //NO_APP check_reg!(v0_f32 f32 "s0" "fmov"); -// CHECK-LABEL: v0_i64: +// CHECK-LABEL: {{("#)?}}v0_i64{{"?}} // CHECK: //APP // CHECK: fmov s0, s0 // CHECK: //NO_APP check_reg!(v0_i64 i64 "s0" "fmov"); -// CHECK-LABEL: v0_f64: +// CHECK-LABEL: {{("#)?}}v0_f64{{"?}} // CHECK: //APP // CHECK: fmov s0, s0 // CHECK: //NO_APP check_reg!(v0_f64 f64 "s0" "fmov"); -// CHECK-LABEL: v0_ptr: +// CHECK-LABEL: {{("#)?}}v0_ptr{{"?}} // CHECK: //APP // CHECK: fmov s0, s0 // CHECK: //NO_APP check_reg!(v0_ptr ptr "s0" "fmov"); -// CHECK-LABEL: v0_i8x8: +// CHECK-LABEL: {{("#)?}}v0_i8x8{{"?}} // CHECK: //APP // CHECK: fmov s0, s0 // CHECK: //NO_APP check_reg!(v0_i8x8 i8x8 "s0" "fmov"); -// CHECK-LABEL: v0_i16x4: +// CHECK-LABEL: {{("#)?}}v0_i16x4{{"?}} // CHECK: //APP // CHECK: fmov s0, s0 // CHECK: //NO_APP check_reg!(v0_i16x4 i16x4 "s0" "fmov"); -// CHECK-LABEL: v0_i32x2: +// CHECK-LABEL: {{("#)?}}v0_i32x2{{"?}} // CHECK: //APP // CHECK: fmov s0, s0 // CHECK: //NO_APP check_reg!(v0_i32x2 i32x2 "s0" "fmov"); -// CHECK-LABEL: v0_i64x1: +// CHECK-LABEL: {{("#)?}}v0_i64x1{{"?}} // CHECK: //APP // CHECK: fmov s0, s0 // CHECK: //NO_APP check_reg!(v0_i64x1 i64x1 "s0" "fmov"); -// CHECK-LABEL: v0_f32x2: +// CHECK-LABEL: {{("#)?}}v0_f32x2{{"?}} // CHECK: //APP // CHECK: fmov s0, s0 // CHECK: //NO_APP check_reg!(v0_f32x2 f32x2 "s0" "fmov"); -// CHECK-LABEL: v0_f64x1: +// CHECK-LABEL: {{("#)?}}v0_f64x1{{"?}} // CHECK: //APP // CHECK: fmov s0, s0 // CHECK: //NO_APP check_reg!(v0_f64x1 f64x1 "s0" "fmov"); -// CHECK-LABEL: v0_i8x16: +// CHECK-LABEL: {{("#)?}}v0_i8x16{{"?}} // CHECK: //APP // CHECK: fmov s0, s0 // CHECK: //NO_APP check_reg!(v0_i8x16 i8x16 "s0" "fmov"); -// CHECK-LABEL: v0_i16x8: +// CHECK-LABEL: {{("#)?}}v0_i16x8{{"?}} // CHECK: //APP // CHECK: fmov s0, s0 // CHECK: //NO_APP check_reg!(v0_i16x8 i16x8 "s0" "fmov"); -// CHECK-LABEL: v0_i32x4: +// CHECK-LABEL: {{("#)?}}v0_i32x4{{"?}} // CHECK: //APP // CHECK: fmov s0, s0 // CHECK: //NO_APP check_reg!(v0_i32x4 i32x4 "s0" "fmov"); -// CHECK-LABEL: v0_i64x2: +// CHECK-LABEL: {{("#)?}}v0_i64x2{{"?}} // CHECK: //APP // CHECK: fmov s0, s0 // CHECK: //NO_APP check_reg!(v0_i64x2 i64x2 "s0" "fmov"); -// CHECK-LABEL: v0_f32x4: +// CHECK-LABEL: {{("#)?}}v0_f32x4{{"?}} // CHECK: //APP // CHECK: fmov s0, s0 // CHECK: //NO_APP check_reg!(v0_f32x4 f32x4 "s0" "fmov"); -// CHECK-LABEL: v0_f64x2: +// CHECK-LABEL: {{("#)?}}v0_f64x2{{"?}} // CHECK: //APP // CHECK: fmov s0, s0 // CHECK: //NO_APP diff --git a/tests/codegen/ascii-char.rs b/tests/codegen/ascii-char.rs index fab9f8632fc..86ec9d73afe 100644 --- a/tests/codegen/ascii-char.rs +++ b/tests/codegen/ascii-char.rs @@ -12,7 +12,7 @@ pub fn unwrap_digit_from_remainder(v: u32) -> AsciiChar { // CHECK-NOT: panic // CHECK: %[[R:.+]] = urem i32 %v, 10 - // CHECK-NEXT: %[[T:.+]] = trunc i32 %[[R]] to i8 + // CHECK-NEXT: %[[T:.+]] = trunc{{( nuw)?( nsw)?}} i32 %[[R]] to i8 // CHECK-NEXT: %[[D:.+]] = or{{( disjoint)?}} i8 %[[T]], 48 // CHECK-NEXT: ret i8 %[[D]] diff --git a/tests/codegen/cffi/c-variadic-naked.rs b/tests/codegen/cffi/c-variadic-naked.rs new file mode 100644 index 00000000000..807873ea368 --- /dev/null +++ b/tests/codegen/cffi/c-variadic-naked.rs @@ -0,0 +1,19 @@ +//@ needs-asm-support +//@ only-x86_64 + +// tests that `va_start` is not injected into naked functions + +#![crate_type = "lib"] +#![feature(c_variadic)] +#![feature(naked_functions)] +#![no_std] + +#[naked] +pub unsafe extern "C" fn c_variadic(_: usize, _: ...) { + // CHECK-NOT: va_start + // CHECK-NOT: alloca + core::arch::asm! { + "ret", + options(noreturn), + } +} diff --git a/tests/codegen/float/f128.rs b/tests/codegen/float/f128.rs new file mode 100644 index 00000000000..97d545e0283 --- /dev/null +++ b/tests/codegen/float/f128.rs @@ -0,0 +1,129 @@ +// Verify that our intrinsics generate the correct LLVM calls for f128 + +#![crate_type = "lib"] +#![feature(f128)] +#![feature(core_intrinsics)] + +// CHECK-LABEL: i1 @f128_eq( +#[no_mangle] +pub fn f128_eq(a: f128, b: f128) -> bool { + // CHECK: fcmp oeq fp128 %{{.+}}, %{{.+}} + a == b +} + +// CHECK-LABEL: i1 @f128_ne( +#[no_mangle] +pub fn f128_ne(a: f128, b: f128) -> bool { + // CHECK: fcmp une fp128 %{{.+}}, %{{.+}} + a != b +} + +// CHECK-LABEL: i1 @f128_gt( +#[no_mangle] +pub fn f128_gt(a: f128, b: f128) -> bool { + // CHECK: fcmp ogt fp128 %{{.+}}, %{{.+}} + a > b +} + +// CHECK-LABEL: i1 @f128_ge( +#[no_mangle] +pub fn f128_ge(a: f128, b: f128) -> bool { + // CHECK: fcmp oge fp128 %{{.+}}, %{{.+}} + a >= b +} + +// CHECK-LABEL: i1 @f128_lt( +#[no_mangle] +pub fn f128_lt(a: f128, b: f128) -> bool { + // CHECK: fcmp olt fp128 %{{.+}}, %{{.+}} + a < b +} + +// CHECK-LABEL: i1 @f128_le( +#[no_mangle] +pub fn f128_le(a: f128, b: f128) -> bool { + // CHECK: fcmp ole fp128 %{{.+}}, %{{.+}} + a <= b +} + +// CHECK-LABEL: fp128 @f128_neg( +#[no_mangle] +pub fn f128_neg(a: f128) -> f128 { + // CHECK: fneg fp128 + -a +} + +// CHECK-LABEL: fp128 @f128_add( +#[no_mangle] +pub fn f128_add(a: f128, b: f128) -> f128 { + // CHECK: fadd fp128 %{{.+}}, %{{.+}} + a + b +} + +// CHECK-LABEL: fp128 @f128_sub( +#[no_mangle] +pub fn f128_sub(a: f128, b: f128) -> f128 { + // CHECK: fsub fp128 %{{.+}}, %{{.+}} + a - b +} + +// CHECK-LABEL: fp128 @f128_mul( +#[no_mangle] +pub fn f128_mul(a: f128, b: f128) -> f128 { + // CHECK: fmul fp128 %{{.+}}, %{{.+}} + a * b +} + +// CHECK-LABEL: fp128 @f128_div( +#[no_mangle] +pub fn f128_div(a: f128, b: f128) -> f128 { + // CHECK: fdiv fp128 %{{.+}}, %{{.+}} + a / b +} + +// CHECK-LABEL: fp128 @f128_rem( +#[no_mangle] +pub fn f128_rem(a: f128, b: f128) -> f128 { + // CHECK: frem fp128 %{{.+}}, %{{.+}} + a % b +} + +// CHECK-LABEL: void @f128_add_assign( +#[no_mangle] +pub fn f128_add_assign(a: &mut f128, b: f128) { + // CHECK: fadd fp128 %{{.+}}, %{{.+}} + // CHECK-NEXT: store fp128 %{{.+}}, ptr %{{.+}} + *a += b; +} + +// CHECK-LABEL: void @f128_sub_assign( +#[no_mangle] +pub fn f128_sub_assign(a: &mut f128, b: f128) { + // CHECK: fsub fp128 %{{.+}}, %{{.+}} + // CHECK-NEXT: store fp128 %{{.+}}, ptr %{{.+}} + *a -= b; +} + +// CHECK-LABEL: void @f128_mul_assign( +#[no_mangle] +pub fn f128_mul_assign(a: &mut f128, b: f128) { + // CHECK: fmul fp128 %{{.+}}, %{{.+}} + // CHECK-NEXT: store fp128 %{{.+}}, ptr %{{.+}} + *a *= b +} + +// CHECK-LABEL: void @f128_div_assign( +#[no_mangle] +pub fn f128_div_assign(a: &mut f128, b: f128) { + // CHECK: fdiv fp128 %{{.+}}, %{{.+}} + // CHECK-NEXT: store fp128 %{{.+}}, ptr %{{.+}} + *a /= b +} + +// CHECK-LABEL: void @f128_rem_assign( +#[no_mangle] +pub fn f128_rem_assign(a: &mut f128, b: f128) { + // CHECK: frem fp128 %{{.+}}, %{{.+}} + // CHECK-NEXT: store fp128 %{{.+}}, ptr %{{.+}} + *a %= b +} diff --git a/tests/codegen/float/f16.rs b/tests/codegen/float/f16.rs new file mode 100644 index 00000000000..d1f75cc3b68 --- /dev/null +++ b/tests/codegen/float/f16.rs @@ -0,0 +1,129 @@ +// Verify that our intrinsics generate the correct LLVM calls for f16 + +#![crate_type = "lib"] +#![feature(f16)] +#![feature(core_intrinsics)] + +// CHECK-LABEL: i1 @f16_eq( +#[no_mangle] +pub fn f16_eq(a: f16, b: f16) -> bool { + // CHECK: fcmp oeq half %{{.+}}, %{{.+}} + a == b +} + +// CHECK-LABEL: i1 @f16_ne( +#[no_mangle] +pub fn f16_ne(a: f16, b: f16) -> bool { + // CHECK: fcmp une half %{{.+}}, %{{.+}} + a != b +} + +// CHECK-LABEL: i1 @f16_gt( +#[no_mangle] +pub fn f16_gt(a: f16, b: f16) -> bool { + // CHECK: fcmp ogt half %{{.+}}, %{{.+}} + a > b +} + +// CHECK-LABEL: i1 @f16_ge( +#[no_mangle] +pub fn f16_ge(a: f16, b: f16) -> bool { + // CHECK: fcmp oge half %{{.+}}, %{{.+}} + a >= b +} + +// CHECK-LABEL: i1 @f16_lt( +#[no_mangle] +pub fn f16_lt(a: f16, b: f16) -> bool { + // CHECK: fcmp olt half %{{.+}}, %{{.+}} + a < b +} + +// CHECK-LABEL: i1 @f16_le( +#[no_mangle] +pub fn f16_le(a: f16, b: f16) -> bool { + // CHECK: fcmp ole half %{{.+}}, %{{.+}} + a <= b +} + +// CHECK-LABEL: half @f16_neg( +#[no_mangle] +pub fn f16_neg(a: f16) -> f16 { + // CHECK: fneg half %{{.+}} + -a +} + +// CHECK-LABEL: half @f16_add( +#[no_mangle] +pub fn f16_add(a: f16, b: f16) -> f16 { + // CHECK: fadd half %{{.+}}, %{{.+}} + a + b +} + +// CHECK-LABEL: half @f16_sub( +#[no_mangle] +pub fn f16_sub(a: f16, b: f16) -> f16 { + // CHECK: fsub half %{{.+}}, %{{.+}} + a - b +} + +// CHECK-LABEL: half @f16_mul( +#[no_mangle] +pub fn f16_mul(a: f16, b: f16) -> f16 { + // CHECK: fmul half %{{.+}}, %{{.+}} + a * b +} + +// CHECK-LABEL: half @f16_div( +#[no_mangle] +pub fn f16_div(a: f16, b: f16) -> f16 { + // CHECK: fdiv half %{{.+}}, %{{.+}} + a / b +} + +// CHECK-LABEL: half @f16_rem( +#[no_mangle] +pub fn f16_rem(a: f16, b: f16) -> f16 { + // CHECK: frem half %{{.+}}, %{{.+}} + a % b +} + +// CHECK-LABEL: void @f16_add_assign( +#[no_mangle] +pub fn f16_add_assign(a: &mut f16, b: f16) { + // CHECK: fadd half %{{.+}}, %{{.+}} + // CHECK-NEXT: store half %{{.+}}, ptr %{{.+}} + *a += b; +} + +// CHECK-LABEL: void @f16_sub_assign( +#[no_mangle] +pub fn f16_sub_assign(a: &mut f16, b: f16) { + // CHECK: fsub half %{{.+}}, %{{.+}} + // CHECK-NEXT: store half %{{.+}}, ptr %{{.+}} + *a -= b; +} + +// CHECK-LABEL: void @f16_mul_assign( +#[no_mangle] +pub fn f16_mul_assign(a: &mut f16, b: f16) { + // CHECK: fmul half %{{.+}}, %{{.+}} + // CHECK-NEXT: store half %{{.+}}, ptr %{{.+}} + *a *= b +} + +// CHECK-LABEL: void @f16_div_assign( +#[no_mangle] +pub fn f16_div_assign(a: &mut f16, b: f16) { + // CHECK: fdiv half %{{.+}}, %{{.+}} + // CHECK-NEXT: store half %{{.+}}, ptr %{{.+}} + *a /= b +} + +// CHECK-LABEL: void @f16_rem_assign( +#[no_mangle] +pub fn f16_rem_assign(a: &mut f16, b: f16) { + // CHECK: frem half %{{.+}}, %{{.+}} + // CHECK-NEXT: store half %{{.+}}, ptr %{{.+}} + *a %= b +} diff --git a/tests/codegen/naked-fn/naked-functions.rs b/tests/codegen/naked-fn/naked-functions.rs index 755dd155112..3c426825537 100644 --- a/tests/codegen/naked-fn/naked-functions.rs +++ b/tests/codegen/naked-fn/naked-functions.rs @@ -19,7 +19,7 @@ pub unsafe extern "C" fn naked_empty() { } // CHECK: Function Attrs: naked -// CHECK-NEXT: define{{.*}}i{{[0-9]+}} @naked_with_args_and_return(i64 %a, i64 %b) +// CHECK-NEXT: define{{.*}}i{{[0-9]+}} @naked_with_args_and_return(i64 %0, i64 %1) #[no_mangle] #[naked] pub unsafe extern "C" fn naked_with_args_and_return(a: isize, b: isize) -> isize { diff --git a/tests/codegen/unchecked_shifts.rs b/tests/codegen/unchecked_shifts.rs index 7d020fbb4d2..86517c89627 100644 --- a/tests/codegen/unchecked_shifts.rs +++ b/tests/codegen/unchecked_shifts.rs @@ -22,7 +22,7 @@ pub unsafe fn unchecked_shl_unsigned_smaller(a: u16, b: u32) -> u16 { // CHECK-DAG: %[[INRANGE:.+]] = icmp ult i32 %b, 16 // CHECK-DAG: tail call void @llvm.assume(i1 %[[INRANGE]]) - // CHECK-DAG: %[[TRUNC:.+]] = trunc i32 %b to i16 + // CHECK-DAG: %[[TRUNC:.+]] = trunc{{( nuw)?( nsw)?}} i32 %b to i16 // CHECK-DAG: shl i16 %a, %[[TRUNC]] a.unchecked_shl(b) } @@ -54,7 +54,7 @@ pub unsafe fn unchecked_shr_signed_smaller(a: i16, b: u32) -> i16 { // CHECK-DAG: %[[INRANGE:.+]] = icmp ult i32 %b, 16 // CHECK-DAG: tail call void @llvm.assume(i1 %[[INRANGE]]) - // CHECK-DAG: %[[TRUNC:.+]] = trunc i32 %b to i16 + // CHECK-DAG: %[[TRUNC:.+]] = trunc{{( nuw)?( nsw)?}} i32 %b to i16 // CHECK-DAG: ashr i16 %a, %[[TRUNC]] a.unchecked_shr(b) } @@ -94,7 +94,7 @@ pub unsafe fn unchecked_shl_u8_i128(a: u8, b: i128) -> u8 { // CHECK-DAG: %[[INRANGE:.+]] = icmp ult i128 %b, 8 // CHECK-DAG: tail call void @llvm.assume(i1 %[[INRANGE]]) - // CHECK-DAG: %[[TRUNC:.+]] = trunc i128 %b to i8 + // CHECK-DAG: %[[TRUNC:.+]] = trunc{{( nuw)?( nsw)?}} i128 %b to i8 // CHECK-DAG: shl i8 %a, %[[TRUNC]] std::intrinsics::unchecked_shl(a, b) } @@ -107,7 +107,7 @@ pub unsafe fn unchecked_shr_i8_u128(a: i8, b: u128) -> i8 { // CHECK-DAG: %[[INRANGE:.+]] = icmp ult i128 %b, 8 // CHECK-DAG: tail call void @llvm.assume(i1 %[[INRANGE]]) - // CHECK-DAG: %[[TRUNC:.+]] = trunc i128 %b to i8 + // CHECK-DAG: %[[TRUNC:.+]] = trunc{{( nuw)?( nsw)?}} i128 %b to i8 // CHECK-DAG: ashr i8 %a, %[[TRUNC]] std::intrinsics::unchecked_shr(a, b) } diff --git a/tests/run-make/compiler-builtins/rmake.rs b/tests/run-make/compiler-builtins/rmake.rs index e5939470b46..92d6895143c 100644 --- a/tests/run-make/compiler-builtins/rmake.rs +++ b/tests/run-make/compiler-builtins/rmake.rs @@ -22,6 +22,7 @@ use run_make_support::object::read::Object; use run_make_support::object::ObjectSection; use run_make_support::object::ObjectSymbol; use run_make_support::object::RelocationTarget; +use run_make_support::set_host_rpath; use run_make_support::tmp_dir; use std::collections::HashSet; @@ -48,8 +49,8 @@ fn main() { let path = std::env::var("PATH").unwrap(); let rustc = std::env::var("RUSTC").unwrap(); let bootstrap_cargo = std::env::var("BOOTSTRAP_CARGO").unwrap(); - let status = std::process::Command::new(bootstrap_cargo) - .args([ + let mut cmd = std::process::Command::new(bootstrap_cargo); + cmd.args([ "build", "--manifest-path", manifest_path.to_str().unwrap(), @@ -62,10 +63,10 @@ fn main() { .env("RUSTC", rustc) .env("RUSTFLAGS", "-Copt-level=0 -Cdebug-assertions=yes") .env("CARGO_TARGET_DIR", &target_dir) - .env("RUSTC_BOOTSTRAP", "1") - .status() - .unwrap(); + .env("RUSTC_BOOTSTRAP", "1"); + set_host_rpath(&mut cmd); + let status = cmd.status().unwrap(); assert!(status.success()); let rlibs_path = target_dir.join(target).join("debug").join("deps"); diff --git a/tests/run-make/rust-lld/Makefile b/tests/run-make/rust-lld/Makefile index f8526530d4d..1ecac479f41 100644 --- a/tests/run-make/rust-lld/Makefile +++ b/tests/run-make/rust-lld/Makefile @@ -4,5 +4,9 @@ include ../tools.mk # needs-rust-lld # ignore-s390x lld does not yet support s390x as target all: - RUSTC_LOG=rustc_codegen_ssa::back::link=info $(RUSTC) -Clink-self-contained=+linker -Clinker-flavor=gnu-lld-cc -Zunstable-options -Clink-args=-Wl,-v main.rs 2> $(TMPDIR)/output.txt + RUSTC_LOG=rustc_codegen_ssa::back::link=info $(RUSTC) -Clink-self-contained=+linker -Zlinker-features=+lld -Zunstable-options -Clink-args=-Wl,-v main.rs 2> $(TMPDIR)/output.txt + $(CGREP) -e "^LLD [0-9]+\.[0-9]+\.[0-9]+" < $(TMPDIR)/output.txt + + # while we're here, also check that the last linker feature flag "wins" + RUSTC_LOG=rustc_codegen_ssa::back::link=info $(RUSTC) -Clink-self-contained=+linker -Zlinker-features=-lld -Zlinker-features=+lld -Zunstable-options -Clink-args=-Wl,-v main.rs 2> $(TMPDIR)/output.txt $(CGREP) -e "^LLD [0-9]+\.[0-9]+\.[0-9]+" < $(TMPDIR)/output.txt diff --git a/tests/rustdoc-ui/auxiliary/include-str-bare-urls.md b/tests/rustdoc-ui/auxiliary/include-str-bare-urls.md new file mode 100644 index 00000000000..b07717d8f02 --- /dev/null +++ b/tests/rustdoc-ui/auxiliary/include-str-bare-urls.md @@ -0,0 +1,10 @@ +HEADS UP! https://example.com MUST SHOW UP IN THE STDERR FILE! + +Normally, a line with errors on it will also have a comment +marking it up as something that needs to generate an error. + +The test harness doesn't gather hot comments from this file. +Rustdoc will generate an error for the line, and the `.stderr` +snapshot includes this error, but Compiletest doesn't see it. + +If the stderr file changes, make sure the warning points at the URL! diff --git a/tests/rustdoc-ui/include-str-bare-urls.rs b/tests/rustdoc-ui/include-str-bare-urls.rs new file mode 100644 index 00000000000..c452c88cdd3 --- /dev/null +++ b/tests/rustdoc-ui/include-str-bare-urls.rs @@ -0,0 +1,15 @@ +// https://github.com/rust-lang/rust/issues/118549 +// +// HEADS UP! +// +// Normally, a line with errors on it will also have a comment +// marking it up as something that needs to generate an error. +// +// The test harness doesn't gather hot comments from the `.md` file. +// Rustdoc will generate an error for the line, and the `.stderr` +// snapshot includes this error, but Compiletest doesn't see it. +// +// If the stderr file changes, make sure the warning points at the URL! + +#![deny(rustdoc::bare_urls)] +#![doc=include_str!("auxiliary/include-str-bare-urls.md")] diff --git a/tests/rustdoc-ui/include-str-bare-urls.stderr b/tests/rustdoc-ui/include-str-bare-urls.stderr new file mode 100644 index 00000000000..a4234196b23 --- /dev/null +++ b/tests/rustdoc-ui/include-str-bare-urls.stderr @@ -0,0 +1,15 @@ +error: this URL is not a hyperlink + --> $DIR/auxiliary/include-str-bare-urls.md:1:11 + | +LL | HEADS UP! https://example.com MUST SHOW UP IN THE STDERR FILE! + | ^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `<https://example.com>` + | + = note: bare URLs are not automatically turned into clickable links +note: the lint level is defined here + --> $DIR/include-str-bare-urls.rs:14:9 + | +LL | #![deny(rustdoc::bare_urls)] + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/rustdoc-ui/intra-doc/warning.rs b/tests/rustdoc-ui/intra-doc/warning.rs index 96b5c2b36a1..ed51b2fa41b 100644 --- a/tests/rustdoc-ui/intra-doc/warning.rs +++ b/tests/rustdoc-ui/intra-doc/warning.rs @@ -47,11 +47,11 @@ pub fn d() {} macro_rules! f { ($f:expr) => { - #[doc = $f] //~ WARNING `BarF` + #[doc = $f] pub fn f() {} } } -f!("Foo\nbar [BarF] bar\nbaz"); +f!("Foo\nbar [BarF] bar\nbaz"); //~ WARNING `BarF` /** # for example, * diff --git a/tests/rustdoc-ui/intra-doc/warning.stderr b/tests/rustdoc-ui/intra-doc/warning.stderr index 19399a0df5b..3a06f1787e0 100644 --- a/tests/rustdoc-ui/intra-doc/warning.stderr +++ b/tests/rustdoc-ui/intra-doc/warning.stderr @@ -69,10 +69,10 @@ LL | bar [BarC] bar = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `BarD` - --> $DIR/warning.rs:45:1 + --> $DIR/warning.rs:45:9 | LL | #[doc = "Foo\nbar [BarD] bar\nbaz"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: the link appears in this line: @@ -82,13 +82,10 @@ LL | #[doc = "Foo\nbar [BarD] bar\nbaz"] = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `BarF` - --> $DIR/warning.rs:50:9 + --> $DIR/warning.rs:54:4 | -LL | #[doc = $f] - | ^^^^^^^^^^^ -... LL | f!("Foo\nbar [BarF] bar\nbaz"); - | ------------------------------ in this macro invocation + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: the link appears in this line: @@ -115,10 +112,10 @@ LL | * time to introduce a link [error] = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `error` - --> $DIR/warning.rs:68:1 + --> $DIR/warning.rs:68:9 | LL | #[doc = "single line [error]"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^ | = note: the link appears in this line: @@ -128,10 +125,10 @@ LL | #[doc = "single line [error]"] = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `error` - --> $DIR/warning.rs:71:1 + --> $DIR/warning.rs:71:9 | LL | #[doc = "single line with \"escaping\" [error]"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: the link appears in this line: diff --git a/tests/rustdoc-ui/invalid-syntax.stderr b/tests/rustdoc-ui/invalid-syntax.stderr index 6140a06c555..46d7cdb4f7e 100644 --- a/tests/rustdoc-ui/invalid-syntax.stderr +++ b/tests/rustdoc-ui/invalid-syntax.stderr @@ -90,12 +90,13 @@ LL | | /// ``` = note: error from rustc: unknown start of token: \ warning: could not parse code block as Rust code - --> $DIR/invalid-syntax.rs:70:1 + --> $DIR/invalid-syntax.rs:70:9 | -LL | / #[doc = "```"] +LL | #[doc = "```"] + | _________^ LL | | /// \_ LL | | #[doc = "```"] - | |______________^ + | |_____________^ | = help: mark blocks that do not contain Rust code as text: ```text = note: error from rustc: unknown start of token: \ diff --git a/tests/rustdoc-ui/unescaped_backticks.stderr b/tests/rustdoc-ui/unescaped_backticks.stderr index 000a5b597d2..67b87f353a1 100644 --- a/tests/rustdoc-ui/unescaped_backticks.stderr +++ b/tests/rustdoc-ui/unescaped_backticks.stderr @@ -640,10 +640,10 @@ LL | /// or even to add a number `n` to 42 (`add(42, n)\`)! | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:108:1 + --> $DIR/unescaped_backticks.rs:108:9 | LL | #[doc = "`"] - | ^^^^^^^^^^^^ + | ^^^ | = help: the opening or closing backtick of an inline code may be missing = help: if you meant to use a literal backtick, escape it @@ -651,10 +651,10 @@ LL | #[doc = "`"] to this: \` error: unescaped backtick - --> $DIR/unescaped_backticks.rs:115:1 + --> $DIR/unescaped_backticks.rs:115:9 | LL | #[doc = concat!("\\", "`")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ | = help: the opening backtick of an inline code may be missing change: \` @@ -664,10 +664,10 @@ LL | #[doc = concat!("\\", "`")] to this: \\` error: unescaped backtick - --> $DIR/unescaped_backticks.rs:119:1 + --> $DIR/unescaped_backticks.rs:119:9 | LL | #[doc = "Addition is commutative, which means that add(a, b)` is the same as `add(b, a)`."] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: the opening backtick of a previous inline code may be missing change: Addition is commutative, which means that add(a, b)` is the same as `add(b, a)`. @@ -677,10 +677,10 @@ LL | #[doc = "Addition is commutative, which means that add(a, b)` is the same a to this: Addition is commutative, which means that add(a, b)` is the same as `add(b, a)\`. error: unescaped backtick - --> $DIR/unescaped_backticks.rs:123:1 + --> $DIR/unescaped_backticks.rs:123:9 | LL | #[doc = "Addition is commutative, which means that `add(a, b) is the same as `add(b, a)`."] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: a previous inline code might be longer than expected change: Addition is commutative, which means that `add(a, b) is the same as `add(b, a)`. @@ -690,10 +690,10 @@ LL | #[doc = "Addition is commutative, which means that `add(a, b) is the same a to this: Addition is commutative, which means that `add(a, b) is the same as `add(b, a)\`. error: unescaped backtick - --> $DIR/unescaped_backticks.rs:127:1 + --> $DIR/unescaped_backticks.rs:127:9 | LL | #[doc = "Addition is commutative, which means that `add(a, b)` is the same as add(b, a)`."] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: the opening backtick of an inline code may be missing change: Addition is commutative, which means that `add(a, b)` is the same as add(b, a)`. @@ -703,10 +703,10 @@ LL | #[doc = "Addition is commutative, which means that `add(a, b)` is the same to this: Addition is commutative, which means that `add(a, b)` is the same as add(b, a)\`. error: unescaped backtick - --> $DIR/unescaped_backticks.rs:131:1 + --> $DIR/unescaped_backticks.rs:131:9 | LL | #[doc = "Addition is commutative, which means that `add(a, b)` is the same as `add(b, a)."] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: the closing backtick of an inline code may be missing change: Addition is commutative, which means that `add(a, b)` is the same as `add(b, a). diff --git a/tests/rustdoc/inline_cross/inline_hidden.rs b/tests/rustdoc/inline_cross/inline_hidden.rs index ec06f2f0c5d..2a3dd72749c 100644 --- a/tests/rustdoc/inline_cross/inline_hidden.rs +++ b/tests/rustdoc/inline_cross/inline_hidden.rs @@ -4,9 +4,23 @@ extern crate rustdoc_hidden; +// @has inline_hidden/index.html +// Ensures this item is not inlined. +// @has - '//*[@id="reexport.Foo"]/code' 'pub use rustdoc_hidden::Foo;' #[doc(no_inline)] pub use rustdoc_hidden::Foo; +// Even if the foreign item has `doc(hidden)`, we should be able to inline it. +// @has - '//*[@class="item-name"]/a[@class="struct"]' 'Inlined' +#[doc(inline)] +pub use rustdoc_hidden::Foo as Inlined; + +// Even with this import, we should not see `Foo`. +// @count - '//*[@class="item-name"]' 4 +// @has - '//*[@class="item-name"]/a[@class="struct"]' 'Bar' +// @has - '//*[@class="item-name"]/a[@class="fn"]' 'foo' +pub use rustdoc_hidden::*; + // @has inline_hidden/fn.foo.html // @!has - '//a/@title' 'Foo' pub fn foo(_: Foo) {} diff --git a/tests/ui/array-slice-vec/vector-no-ann.stderr b/tests/ui/array-slice-vec/vector-no-ann.stderr index 24b6abfb342..716971eb120 100644 --- a/tests/ui/array-slice-vec/vector-no-ann.stderr +++ b/tests/ui/array-slice-vec/vector-no-ann.stderr @@ -1,4 +1,4 @@ -error[E0282]: type annotations needed for `Vec<T>` +error[E0282]: type annotations needed for `Vec<_>` --> $DIR/vector-no-ann.rs:2:9 | LL | let _foo = Vec::new(); diff --git a/tests/ui/asm/aarch64/type-check-2-2.stderr b/tests/ui/asm/aarch64/type-check-2-2.stderr index 41f7c01dc82..760aaefac83 100644 --- a/tests/ui/asm/aarch64/type-check-2-2.stderr +++ b/tests/ui/asm/aarch64/type-check-2-2.stderr @@ -8,8 +8,8 @@ LL | asm!("{}", in(reg) x); | help: consider assigning a value | -LL | let x: u64 = 0; - | +++ +LL | let x: u64 = 42; + | ++++ error[E0381]: used binding `y` isn't initialized --> $DIR/type-check-2-2.rs:22:9 @@ -21,8 +21,8 @@ LL | asm!("{}", inout(reg) y); | help: consider assigning a value | -LL | let mut y: u64 = 0; - | +++ +LL | let mut y: u64 = 42; + | ++++ error[E0596]: cannot borrow `v` as mutable, as it is not declared as mutable --> $DIR/type-check-2-2.rs:28:13 diff --git a/tests/ui/asm/x86_64/type-check-5.stderr b/tests/ui/asm/x86_64/type-check-5.stderr index 7970e76d6a1..4fb75993463 100644 --- a/tests/ui/asm/x86_64/type-check-5.stderr +++ b/tests/ui/asm/x86_64/type-check-5.stderr @@ -8,8 +8,8 @@ LL | asm!("{}", in(reg) x); | help: consider assigning a value | -LL | let x: u64 = 0; - | +++ +LL | let x: u64 = 42; + | ++++ error[E0381]: used binding `y` isn't initialized --> $DIR/type-check-5.rs:18:9 @@ -21,8 +21,8 @@ LL | asm!("{}", inout(reg) y); | help: consider assigning a value | -LL | let mut y: u64 = 0; - | +++ +LL | let mut y: u64 = 42; + | ++++ error[E0596]: cannot borrow `v` as mutable, as it is not declared as mutable --> $DIR/type-check-5.rs:24:13 diff --git a/tests/ui/associated-types/associated-types-outlives.rs b/tests/ui/associated-types/associated-types-outlives.rs index 55c276280b9..245218067b4 100644 --- a/tests/ui/associated-types/associated-types-outlives.rs +++ b/tests/ui/associated-types/associated-types-outlives.rs @@ -3,10 +3,10 @@ // fn body, causing this (invalid) code to be accepted. pub trait Foo<'a> { - type Bar; + type Bar: Clone; } -impl<'a, T:'a> Foo<'a> for T { +impl<'a, T: 'a> Foo<'a> for T { type Bar = &'a T; } diff --git a/tests/ui/associated-types/associated-types-outlives.stderr b/tests/ui/associated-types/associated-types-outlives.stderr index deeedd22266..c97af672c33 100644 --- a/tests/ui/associated-types/associated-types-outlives.stderr +++ b/tests/ui/associated-types/associated-types-outlives.stderr @@ -10,6 +10,11 @@ LL | drop(x); | ^ move out of `x` occurs here LL | return f(y); | - borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | 's: loop { y = denormalise(&x).clone(); break } + | ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/associated-types/issue-25700.stderr b/tests/ui/associated-types/issue-25700.stderr index 4e432c0e702..fb0e63c207a 100644 --- a/tests/ui/associated-types/issue-25700.stderr +++ b/tests/ui/associated-types/issue-25700.stderr @@ -7,6 +7,12 @@ LL | drop(t); | - value moved here LL | drop(t); | ^ value used here after move + | +note: if `S<()>` implemented `Clone`, you could clone the value + --> $DIR/issue-25700.rs:1:1 + | +LL | struct S<T: 'static>(#[allow(dead_code)] Option<&'static T>); + | ^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/async-await/async-borrowck-escaping-closure-error.rs b/tests/ui/async-await/async-borrowck-escaping-closure-error.rs index 1990d0ffe2a..ffb97ca04ac 100644 --- a/tests/ui/async-await/async-borrowck-escaping-closure-error.rs +++ b/tests/ui/async-await/async-borrowck-escaping-closure-error.rs @@ -5,7 +5,6 @@ fn foo() -> Box<dyn std::future::Future<Output = u32>> { let x = 0u32; Box::new((async || x)()) //~^ ERROR cannot return value referencing local variable `x` - //~| ERROR cannot return value referencing temporary value } fn main() { diff --git a/tests/ui/async-await/async-borrowck-escaping-closure-error.stderr b/tests/ui/async-await/async-borrowck-escaping-closure-error.stderr index be67c78221a..4b1ce300b56 100644 --- a/tests/ui/async-await/async-borrowck-escaping-closure-error.stderr +++ b/tests/ui/async-await/async-borrowck-escaping-closure-error.stderr @@ -7,15 +7,6 @@ LL | Box::new((async || x)()) | | `x` is borrowed here | returns a value referencing data owned by the current function -error[E0515]: cannot return value referencing temporary value - --> $DIR/async-borrowck-escaping-closure-error.rs:6:5 - | -LL | Box::new((async || x)()) - | ^^^^^^^^^------------^^^ - | | | - | | temporary value created here - | returns a value referencing data owned by the current function - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0515`. diff --git a/tests/ui/async-await/async-closures/dont-ice-when-body-tainted-by-errors.rs b/tests/ui/async-await/async-closures/dont-ice-when-body-tainted-by-errors.rs new file mode 100644 index 00000000000..8fc9924a12f --- /dev/null +++ b/tests/ui/async-await/async-closures/dont-ice-when-body-tainted-by-errors.rs @@ -0,0 +1,23 @@ +//@ edition: 2021 + +#![feature(async_closure)] + +struct DropMe; + +trait Impossible {} +fn trait_error<T: Impossible>() {} + +pub fn main() { + let b = DropMe; + let async_closure = async move || { + // Type error here taints the environment. This causes us to fallback all + // variables to `Error`. This means that when we compute the upvars for the + // *outer* coroutine-closure, we don't actually see any upvars since `MemCategorization` + // and `ExprUseVisitor`` will bail early when it sees error. This means + // that our underlying assumption that the parent and child captures are + // compatible ends up being broken, previously leading to an ICE. + trait_error::<()>(); + //~^ ERROR the trait bound `(): Impossible` is not satisfied + let _b = b; + }; +} diff --git a/tests/ui/async-await/async-closures/dont-ice-when-body-tainted-by-errors.stderr b/tests/ui/async-await/async-closures/dont-ice-when-body-tainted-by-errors.stderr new file mode 100644 index 00000000000..b4dc3e268bd --- /dev/null +++ b/tests/ui/async-await/async-closures/dont-ice-when-body-tainted-by-errors.stderr @@ -0,0 +1,20 @@ +error[E0277]: the trait bound `(): Impossible` is not satisfied + --> $DIR/dont-ice-when-body-tainted-by-errors.rs:19:23 + | +LL | trait_error::<()>(); + | ^^ the trait `Impossible` is not implemented for `()` + | +help: this trait has no implementations, consider adding one + --> $DIR/dont-ice-when-body-tainted-by-errors.rs:7:1 + | +LL | trait Impossible {} + | ^^^^^^^^^^^^^^^^ +note: required by a bound in `trait_error` + --> $DIR/dont-ice-when-body-tainted-by-errors.rs:8:19 + | +LL | fn trait_error<T: Impossible>() {} + | ^^^^^^^^^^ required by this bound in `trait_error` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/async-await/async-closures/moro-example.rs b/tests/ui/async-await/async-closures/moro-example.rs new file mode 100644 index 00000000000..5a8f42c7ca5 --- /dev/null +++ b/tests/ui/async-await/async-closures/moro-example.rs @@ -0,0 +1,43 @@ +//@ check-pass +//@ edition: 2021 + +#![feature(async_closure)] + +use std::future::Future; +use std::pin::Pin; +use std::{marker::PhantomData, sync::Mutex}; + +type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>; + +pub struct Scope<'scope, 'env: 'scope> { + enqueued: Mutex<Vec<BoxFuture<'scope, ()>>>, + phantom: PhantomData<&'env ()>, +} + +impl<'scope, 'env: 'scope> Scope<'scope, 'env> { + pub fn spawn(&'scope self, future: impl Future<Output = ()> + Send + 'scope) { + self.enqueued.lock().unwrap().push(Box::pin(future)); + } +} + +fn scope_with_closure<'env, B>(_body: B) -> BoxFuture<'env, ()> +where + for<'scope> B: async FnOnce(&'scope Scope<'scope, 'env>), +{ + todo!() +} + +type ScopeRef<'scope, 'env> = &'scope Scope<'scope, 'env>; + +async fn go<'a>(value: &'a i32) { + let closure = async |scope: ScopeRef<'_, 'a>| { + let _future1 = scope.spawn(async { + // Make sure that `*value` is immutably borrowed with lifetime of + // `'a` and not with the lifetime of the containing coroutine-closure. + let _v = *value; + }); + }; + scope_with_closure(closure).await; +} + +fn main() {} diff --git a/tests/ui/async-await/async-closures/no-borrow-from-env.rs b/tests/ui/async-await/async-closures/no-borrow-from-env.rs new file mode 100644 index 00000000000..fe84aeeb32f --- /dev/null +++ b/tests/ui/async-await/async-closures/no-borrow-from-env.rs @@ -0,0 +1,44 @@ +//@ edition: 2021 +//@ check-pass + +#![feature(async_closure)] + +fn outlives<'a>(_: impl Sized + 'a) {} + +async fn call_once(f: impl async FnOnce()) { + f().await; +} + +fn simple<'a>(x: &'a i32) { + let c = async || { println!("{}", *x); }; + outlives::<'a>(c()); + outlives::<'a>(call_once(c)); + + let c = async move || { println!("{}", *x); }; + outlives::<'a>(c()); + outlives::<'a>(call_once(c)); +} + +struct S<'a>(&'a i32); + +fn through_field<'a>(x: S<'a>) { + let c = async || { println!("{}", *x.0); }; + outlives::<'a>(c()); + outlives::<'a>(call_once(c)); + + let c = async move || { println!("{}", *x.0); }; + outlives::<'a>(c()); + outlives::<'a>(call_once(c)); +} + +fn through_field_and_ref<'a>(x: &S<'a>) { + let c = async || { println!("{}", *x.0); }; + outlives::<'a>(c()); + outlives::<'a>(call_once(c)); + + let c = async move || { println!("{}", *x.0); }; + outlives::<'a>(c()); + // outlives::<'a>(call_once(c)); // FIXME(async_closures): Figure out why this fails +} + +fn main() {} diff --git a/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.rs b/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.rs new file mode 100644 index 00000000000..17681161e20 --- /dev/null +++ b/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.rs @@ -0,0 +1,47 @@ +//@ edition: 2018 + +// This is `no-borrow-from-env.rs`, but under edition 2018 we still want to make +// sure that we don't ICE or anything, even if precise closure captures means +// that we can't actually borrowck successfully. + +#![feature(async_closure)] + +fn outlives<'a>(_: impl Sized + 'a) {} + +async fn call_once(f: impl async FnOnce()) { + f().await; +} + +fn simple<'a>(x: &'a i32) { + let c = async || { println!("{}", *x); }; //~ ERROR `x` does not live long enough + outlives::<'a>(c()); + outlives::<'a>(call_once(c)); + + let c = async move || { println!("{}", *x); }; + outlives::<'a>(c()); //~ ERROR `c` does not live long enough + outlives::<'a>(call_once(c)); //~ ERROR cannot move out of `c` +} + +struct S<'a>(&'a i32); + +fn through_field<'a>(x: S<'a>) { + let c = async || { println!("{}", *x.0); }; //~ ERROR `x` does not live long enough + outlives::<'a>(c()); + outlives::<'a>(call_once(c)); + + let c = async move || { println!("{}", *x.0); }; //~ ERROR cannot move out of `x` + outlives::<'a>(c()); //~ ERROR `c` does not live long enough + outlives::<'a>(call_once(c)); //~ ERROR cannot move out of `c` +} + +fn through_field_and_ref<'a>(x: &S<'a>) { + let c = async || { println!("{}", *x.0); }; //~ ERROR `x` does not live long enough + outlives::<'a>(c()); + outlives::<'a>(call_once(c)); //~ ERROR explicit lifetime required in the type of `x` + + let c = async move || { println!("{}", *x.0); }; + outlives::<'a>(c()); //~ ERROR `c` does not live long enough + // outlives::<'a>(call_once(c)); // FIXME(async_closures): Figure out why this fails +} + +fn main() {} diff --git a/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr b/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr new file mode 100644 index 00000000000..569028934cb --- /dev/null +++ b/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr @@ -0,0 +1,152 @@ +error[E0597]: `x` does not live long enough + --> $DIR/without-precise-captures-we-are-powerless.rs:16:13 + | +LL | fn simple<'a>(x: &'a i32) { + | -- lifetime `'a` defined here +LL | let c = async || { println!("{}", *x); }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ borrowed value does not live long enough +LL | outlives::<'a>(c()); +LL | outlives::<'a>(call_once(c)); + | ------------ argument requires that `x` is borrowed for `'a` +... +LL | } + | - `x` dropped here while still borrowed + +error[E0597]: `c` does not live long enough + --> $DIR/without-precise-captures-we-are-powerless.rs:21:20 + | +LL | fn simple<'a>(x: &'a i32) { + | -- lifetime `'a` defined here +... +LL | let c = async move || { println!("{}", *x); }; + | - binding `c` declared here +LL | outlives::<'a>(c()); + | ^-- + | | + | borrowed value does not live long enough + | argument requires that `c` is borrowed for `'a` +LL | outlives::<'a>(call_once(c)); +LL | } + | - `c` dropped here while still borrowed + +error[E0505]: cannot move out of `c` because it is borrowed + --> $DIR/without-precise-captures-we-are-powerless.rs:22:30 + | +LL | fn simple<'a>(x: &'a i32) { + | -- lifetime `'a` defined here +... +LL | let c = async move || { println!("{}", *x); }; + | - binding `c` declared here +LL | outlives::<'a>(c()); + | --- + | | + | borrow of `c` occurs here + | argument requires that `c` is borrowed for `'a` +LL | outlives::<'a>(call_once(c)); + | ^ move out of `c` occurs here + +error[E0597]: `x` does not live long enough + --> $DIR/without-precise-captures-we-are-powerless.rs:28:13 + | +LL | fn through_field<'a>(x: S<'a>) { + | -- lifetime `'a` defined here +LL | let c = async || { println!("{}", *x.0); }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ borrowed value does not live long enough +LL | outlives::<'a>(c()); +LL | outlives::<'a>(call_once(c)); + | ------------ argument requires that `x` is borrowed for `'a` +... +LL | } + | - `x` dropped here while still borrowed + +error[E0505]: cannot move out of `x` because it is borrowed + --> $DIR/without-precise-captures-we-are-powerless.rs:32:13 + | +LL | fn through_field<'a>(x: S<'a>) { + | -- lifetime `'a` defined here +LL | let c = async || { println!("{}", *x.0); }; + | ---------------------------------- borrow of `x` occurs here +LL | outlives::<'a>(c()); +LL | outlives::<'a>(call_once(c)); + | ------------ argument requires that `x` is borrowed for `'a` +LL | +LL | let c = async move || { println!("{}", *x.0); }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ move out of `x` occurs here + +error[E0597]: `c` does not live long enough + --> $DIR/without-precise-captures-we-are-powerless.rs:33:20 + | +LL | fn through_field<'a>(x: S<'a>) { + | -- lifetime `'a` defined here +... +LL | let c = async move || { println!("{}", *x.0); }; + | - binding `c` declared here +LL | outlives::<'a>(c()); + | ^-- + | | + | borrowed value does not live long enough + | argument requires that `c` is borrowed for `'a` +LL | outlives::<'a>(call_once(c)); +LL | } + | - `c` dropped here while still borrowed + +error[E0505]: cannot move out of `c` because it is borrowed + --> $DIR/without-precise-captures-we-are-powerless.rs:34:30 + | +LL | fn through_field<'a>(x: S<'a>) { + | -- lifetime `'a` defined here +... +LL | let c = async move || { println!("{}", *x.0); }; + | - binding `c` declared here +LL | outlives::<'a>(c()); + | --- + | | + | borrow of `c` occurs here + | argument requires that `c` is borrowed for `'a` +LL | outlives::<'a>(call_once(c)); + | ^ move out of `c` occurs here + +error[E0597]: `x` does not live long enough + --> $DIR/without-precise-captures-we-are-powerless.rs:38:13 + | +LL | fn through_field_and_ref<'a>(x: &S<'a>) { + | -- lifetime `'a` defined here +LL | let c = async || { println!("{}", *x.0); }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ borrowed value does not live long enough +LL | outlives::<'a>(c()); +LL | outlives::<'a>(call_once(c)); + | ------------ argument requires that `x` is borrowed for `'a` +... +LL | } + | - `x` dropped here while still borrowed + +error[E0621]: explicit lifetime required in the type of `x` + --> $DIR/without-precise-captures-we-are-powerless.rs:40:20 + | +LL | fn through_field_and_ref<'a>(x: &S<'a>) { + | ------ help: add explicit lifetime `'a` to the type of `x`: `&'a S<'a>` +... +LL | outlives::<'a>(call_once(c)); + | ^^^^^^^^^^^^ lifetime `'a` required + +error[E0597]: `c` does not live long enough + --> $DIR/without-precise-captures-we-are-powerless.rs:43:20 + | +LL | fn through_field_and_ref<'a>(x: &S<'a>) { + | -- lifetime `'a` defined here +... +LL | let c = async move || { println!("{}", *x.0); }; + | - binding `c` declared here +LL | outlives::<'a>(c()); + | ^-- + | | + | borrowed value does not live long enough + | argument requires that `c` is borrowed for `'a` +LL | // outlives::<'a>(call_once(c)); // FIXME(async_closures): Figure out why this fails +LL | } + | - `c` dropped here while still borrowed + +error: aborting due to 10 previous errors + +Some errors have detailed explanations: E0505, E0597, E0621. +For more information about an error, try `rustc --explain E0505`. diff --git a/tests/ui/async-await/dont-ice-for-type-mismatch-in-closure-in-async.rs b/tests/ui/async-await/dont-ice-for-type-mismatch-in-closure-in-async.rs new file mode 100644 index 00000000000..8ad99a4c201 --- /dev/null +++ b/tests/ui/async-await/dont-ice-for-type-mismatch-in-closure-in-async.rs @@ -0,0 +1,17 @@ +//@ edition: 2021 + +fn call(_: impl Fn() -> bool) {} + +async fn test() { + call(|| -> Option<()> { + //~^ ERROR expected + if true { + false + //~^ ERROR mismatched types + } + true + //~^ ERROR mismatched types + }) +} + +fn main() {} diff --git a/tests/ui/async-await/dont-ice-for-type-mismatch-in-closure-in-async.stderr b/tests/ui/async-await/dont-ice-for-type-mismatch-in-closure-in-async.stderr new file mode 100644 index 00000000000..70cd9f924ac --- /dev/null +++ b/tests/ui/async-await/dont-ice-for-type-mismatch-in-closure-in-async.stderr @@ -0,0 +1,46 @@ +error[E0308]: mismatched types + --> $DIR/dont-ice-for-type-mismatch-in-closure-in-async.rs:9:13 + | +LL | / if true { +LL | | false + | | ^^^^^ expected `()`, found `bool` +LL | | +LL | | } + | |_________- expected this to be `()` + +error[E0308]: mismatched types + --> $DIR/dont-ice-for-type-mismatch-in-closure-in-async.rs:12:9 + | +LL | true + | ^^^^ expected `Option<()>`, found `bool` + | + = note: expected enum `Option<()>` + found type `bool` + +error[E0271]: expected `{closure@dont-ice-for-type-mismatch-in-closure-in-async.rs:6:10}` to be a closure that returns `bool`, but it returns `Option<()>` + --> $DIR/dont-ice-for-type-mismatch-in-closure-in-async.rs:6:10 + | +LL | call(|| -> Option<()> { + | _____----_^ + | | | + | | required by a bound introduced by this call +LL | | +LL | | if true { +LL | | false +... | +LL | | +LL | | }) + | |_____^ expected `bool`, found `Option<()>` + | + = note: expected type `bool` + found enum `Option<()>` +note: required by a bound in `call` + --> $DIR/dont-ice-for-type-mismatch-in-closure-in-async.rs:3:25 + | +LL | fn call(_: impl Fn() -> bool) {} + | ^^^^ required by this bound in `call` + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0271, E0308. +For more information about an error, try `rustc --explain E0271`. diff --git a/tests/ui/async-await/track-caller/async-closure-gate.afn.stderr b/tests/ui/async-await/track-caller/async-closure-gate.afn.stderr index 92f38d5a796..640d946421a 100644 --- a/tests/ui/async-await/track-caller/async-closure-gate.afn.stderr +++ b/tests/ui/async-await/track-caller/async-closure-gate.afn.stderr @@ -62,7 +62,7 @@ error[E0308]: mismatched types --> $DIR/async-closure-gate.rs:27:5 | LL | fn foo3() { - | - help: a return type might be missing here: `-> _` + | - help: try adding a return type: `-> impl Future<Output = ()>` LL | / async { LL | | LL | | let _ = #[track_caller] || { @@ -78,7 +78,7 @@ error[E0308]: mismatched types --> $DIR/async-closure-gate.rs:44:5 | LL | fn foo5() { - | - help: a return type might be missing here: `-> _` + | - help: try adding a return type: `-> impl Future<Output = ()>` LL | / async { LL | | LL | | let _ = || { diff --git a/tests/ui/async-await/track-caller/async-closure-gate.nofeat.stderr b/tests/ui/async-await/track-caller/async-closure-gate.nofeat.stderr index 92f38d5a796..640d946421a 100644 --- a/tests/ui/async-await/track-caller/async-closure-gate.nofeat.stderr +++ b/tests/ui/async-await/track-caller/async-closure-gate.nofeat.stderr @@ -62,7 +62,7 @@ error[E0308]: mismatched types --> $DIR/async-closure-gate.rs:27:5 | LL | fn foo3() { - | - help: a return type might be missing here: `-> _` + | - help: try adding a return type: `-> impl Future<Output = ()>` LL | / async { LL | | LL | | let _ = #[track_caller] || { @@ -78,7 +78,7 @@ error[E0308]: mismatched types --> $DIR/async-closure-gate.rs:44:5 | LL | fn foo5() { - | - help: a return type might be missing here: `-> _` + | - help: try adding a return type: `-> impl Future<Output = ()>` LL | / async { LL | | LL | | let _ = || { diff --git a/tests/ui/augmented-assignments.rs b/tests/ui/augmented-assignments.rs index bd2435a78bf..8b263e03593 100644 --- a/tests/ui/augmented-assignments.rs +++ b/tests/ui/augmented-assignments.rs @@ -1,5 +1,6 @@ use std::ops::AddAssign; +#[derive(Clone)] struct Int(i32); impl AddAssign for Int { @@ -16,6 +17,7 @@ fn main() { x; //~^ ERROR cannot move out of `x` because it is borrowed //~| move out of `x` occurs here + //~| HELP consider cloning let y = Int(2); //~^ HELP consider changing this to be mutable diff --git a/tests/ui/augmented-assignments.stderr b/tests/ui/augmented-assignments.stderr index d1096aea279..6b2900dd5d1 100644 --- a/tests/ui/augmented-assignments.stderr +++ b/tests/ui/augmented-assignments.stderr @@ -1,5 +1,5 @@ error[E0505]: cannot move out of `x` because it is borrowed - --> $DIR/augmented-assignments.rs:16:5 + --> $DIR/augmented-assignments.rs:17:5 | LL | let mut x = Int(1); | ----- binding `x` declared here @@ -8,9 +8,14 @@ LL | x ... LL | x; | ^ move out of `x` occurs here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | x.clone(); + | ++++++++ error[E0596]: cannot borrow `y` as mutable, as it is not declared as mutable - --> $DIR/augmented-assignments.rs:23:5 + --> $DIR/augmented-assignments.rs:25:5 | LL | y | ^ cannot borrow as mutable diff --git a/tests/ui/binop/issue-77910-1.stderr b/tests/ui/binop/issue-77910-1.stderr index 6402e568188..74deac900d4 100644 --- a/tests/ui/binop/issue-77910-1.stderr +++ b/tests/ui/binop/issue-77910-1.stderr @@ -32,8 +32,8 @@ LL | xs | help: consider assigning a value | -LL | let xs = todo!(); - | +++++++++ +LL | let xs = &42; + | +++++ error: aborting due to 3 previous errors diff --git a/tests/ui/binop/issue-77910-2.stderr b/tests/ui/binop/issue-77910-2.stderr index a14560ff188..7087f2cdf41 100644 --- a/tests/ui/binop/issue-77910-2.stderr +++ b/tests/ui/binop/issue-77910-2.stderr @@ -21,8 +21,8 @@ LL | xs | help: consider assigning a value | -LL | let xs = todo!(); - | +++++++++ +LL | let xs = &42; + | +++++ error: aborting due to 2 previous errors diff --git a/tests/ui/binop/multiply-is-deref-on-rhs.rs b/tests/ui/binop/multiply-is-deref-on-rhs.rs new file mode 100644 index 00000000000..7c24e1b4d57 --- /dev/null +++ b/tests/ui/binop/multiply-is-deref-on-rhs.rs @@ -0,0 +1,8 @@ +pub fn test(y: &i32) { + let x; + x = () + *y + //~^ ERROR cannot multiply `()` by `&i32` +} + +fn main() {} diff --git a/tests/ui/binop/multiply-is-deref-on-rhs.stderr b/tests/ui/binop/multiply-is-deref-on-rhs.stderr new file mode 100644 index 00000000000..e157f4f58ca --- /dev/null +++ b/tests/ui/binop/multiply-is-deref-on-rhs.stderr @@ -0,0 +1,16 @@ +error[E0369]: cannot multiply `()` by `&i32` + --> $DIR/multiply-is-deref-on-rhs.rs:4:5 + | +LL | x = () + | -- () +LL | *y + | ^- &i32 + | +help: you might have meant to write a semicolon here + | +LL | x = (); + | + + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0369`. diff --git a/tests/ui/borrowck/argument_number_mismatch_ice.stderr b/tests/ui/borrowck/argument_number_mismatch_ice.stderr index 2a6a6dbc64c..702cebb86ba 100644 --- a/tests/ui/borrowck/argument_number_mismatch_ice.stderr +++ b/tests/ui/borrowck/argument_number_mismatch_ice.stderr @@ -12,6 +12,11 @@ error[E0594]: cannot assign to `*input`, which is behind a `&` reference | LL | *input = self.0; | ^^^^^^^^^^^^^^^ `input` is a `&` reference, so the data it refers to cannot be written + | +help: consider changing this to be a mutable reference in the `impl` method and the `trait` definition + | +LL | fn example(&self, input: &mut i32) { + | +++ error: aborting due to 2 previous errors diff --git a/tests/ui/borrowck/borrow-tuple-fields.stderr b/tests/ui/borrowck/borrow-tuple-fields.stderr index e324ebfb50f..8ea7a9a4989 100644 --- a/tests/ui/borrowck/borrow-tuple-fields.stderr +++ b/tests/ui/borrowck/borrow-tuple-fields.stderr @@ -10,6 +10,12 @@ LL | let y = x; LL | LL | r.use_ref(); | - borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL - let r = &x.0; +LL + let r = x.0.clone(); + | error[E0502]: cannot borrow `x.0` as mutable because it is also borrowed as immutable --> $DIR/borrow-tuple-fields.rs:18:13 @@ -42,6 +48,12 @@ LL | let y = x; | ^ move out of `x` occurs here LL | r.use_ref(); | - borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL - let r = &x.0; +LL + let r = x.0.clone(); + | error[E0502]: cannot borrow `x.0` as mutable because it is also borrowed as immutable --> $DIR/borrow-tuple-fields.rs:33:13 diff --git a/tests/ui/borrowck/borrowck-bad-nested-calls-move.stderr b/tests/ui/borrowck/borrowck-bad-nested-calls-move.stderr index e582ec605de..b96949fbb0e 100644 --- a/tests/ui/borrowck/borrowck-bad-nested-calls-move.stderr +++ b/tests/ui/borrowck/borrowck-bad-nested-calls-move.stderr @@ -10,6 +10,12 @@ LL | &*a, | --- borrow of `*a` occurs here LL | a); | ^ move out of `a` occurs here + | +help: consider cloning the value if the performance cost is acceptable + | +LL - &*a, +LL + a.clone(), + | error[E0505]: cannot move out of `a` because it is borrowed --> $DIR/borrowck-bad-nested-calls-move.rs:32:9 @@ -22,6 +28,12 @@ LL | &*a, | --- borrow of `*a` occurs here LL | a); | ^ move out of `a` occurs here + | +help: consider cloning the value if the performance cost is acceptable + | +LL - &*a, +LL + a.clone(), + | error: aborting due to 2 previous errors diff --git a/tests/ui/borrowck/borrowck-block-uninit.stderr b/tests/ui/borrowck/borrowck-block-uninit.stderr index 07c09f1f443..4db98a7a0dc 100644 --- a/tests/ui/borrowck/borrowck-block-uninit.stderr +++ b/tests/ui/borrowck/borrowck-block-uninit.stderr @@ -10,8 +10,8 @@ LL | println!("{}", x); | help: consider assigning a value | -LL | let x: isize = 0; - | +++ +LL | let x: isize = 42; + | ++++ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-break-uninit-2.stderr b/tests/ui/borrowck/borrowck-break-uninit-2.stderr index 7c0cda31c97..e23ca534e74 100644 --- a/tests/ui/borrowck/borrowck-break-uninit-2.stderr +++ b/tests/ui/borrowck/borrowck-break-uninit-2.stderr @@ -10,8 +10,8 @@ LL | println!("{}", x); = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider assigning a value | -LL | let x: isize = 0; - | +++ +LL | let x: isize = 42; + | ++++ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-break-uninit.stderr b/tests/ui/borrowck/borrowck-break-uninit.stderr index 0d879c6fb7d..0367d224f80 100644 --- a/tests/ui/borrowck/borrowck-break-uninit.stderr +++ b/tests/ui/borrowck/borrowck-break-uninit.stderr @@ -10,8 +10,8 @@ LL | println!("{}", x); = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider assigning a value | -LL | let x: isize = 0; - | +++ +LL | let x: isize = 42; + | ++++ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-closures-slice-patterns.stderr b/tests/ui/borrowck/borrowck-closures-slice-patterns.stderr index 411d85b8e05..9066891d298 100644 --- a/tests/ui/borrowck/borrowck-closures-slice-patterns.stderr +++ b/tests/ui/borrowck/borrowck-closures-slice-patterns.stderr @@ -38,6 +38,11 @@ LL | let [y, z @ ..] = x; LL | }; LL | &x; | ^^ value borrowed here after move + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let [y, z @ ..] = x.clone(); + | ++++++++ error[E0502]: cannot borrow `*x` as mutable because it is also borrowed as immutable --> $DIR/borrowck-closures-slice-patterns.rs:33:13 @@ -79,6 +84,12 @@ LL | let [y, z @ ..] = *x; LL | }; LL | &x; | ^^ value borrowed here after move + | +help: consider cloning the value if the performance cost is acceptable + | +LL - let [y, z @ ..] = *x; +LL + let [y, z @ ..] = x.clone(); + | error[E0502]: cannot borrow `*x` as mutable because it is also borrowed as immutable --> $DIR/borrowck-closures-slice-patterns.rs:59:13 diff --git a/tests/ui/borrowck/borrowck-field-sensitivity.stderr b/tests/ui/borrowck/borrowck-field-sensitivity.stderr index 11812847dd1..ea552ff7820 100644 --- a/tests/ui/borrowck/borrowck-field-sensitivity.stderr +++ b/tests/ui/borrowck/borrowck-field-sensitivity.stderr @@ -49,6 +49,12 @@ LL | drop(x.b); | ^^^ move out of `x.b` occurs here LL | drop(**p); | --- borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL - let p = &x.b; +LL + let p = x.b.clone(); + | error[E0505]: cannot move out of `x.b` because it is borrowed --> $DIR/borrowck-field-sensitivity.rs:41:14 @@ -61,6 +67,12 @@ LL | let _y = A { a: 3, .. x }; | ^^^^^^^^^^^^^^^^ move out of `x.b` occurs here LL | drop(**p); | --- borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL - let p = &x.b; +LL + let p = x.b.clone(); + | error[E0499]: cannot borrow `x.a` as mutable more than once at a time --> $DIR/borrowck-field-sensitivity.rs:48:13 diff --git a/tests/ui/borrowck/borrowck-fn-in-const-a.rs b/tests/ui/borrowck/borrowck-fn-in-const-a.rs index d4ceae2963b..d52ec342b1a 100644 --- a/tests/ui/borrowck/borrowck-fn-in-const-a.rs +++ b/tests/ui/borrowck/borrowck-fn-in-const-a.rs @@ -9,4 +9,5 @@ const MOVE: fn(&String) -> String = { }; fn main() { + println!("{}", MOVE(&String::new())); } diff --git a/tests/ui/borrowck/borrowck-fn-in-const-a.stderr b/tests/ui/borrowck/borrowck-fn-in-const-a.stderr index e05696864fd..7bf0f859fdd 100644 --- a/tests/ui/borrowck/borrowck-fn-in-const-a.stderr +++ b/tests/ui/borrowck/borrowck-fn-in-const-a.stderr @@ -3,6 +3,12 @@ error[E0507]: cannot move out of `*x` which is behind a shared reference | LL | return *x | ^^ move occurs because `*x` has type `String`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL - return *x +LL + return x.clone() + | error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-in-static.rs b/tests/ui/borrowck/borrowck-in-static.rs index a45f7b18e07..864dff40f46 100644 --- a/tests/ui/borrowck/borrowck-in-static.rs +++ b/tests/ui/borrowck/borrowck-in-static.rs @@ -1,6 +1,6 @@ // check that borrowck looks inside consts/statics -static FN : &'static (dyn Fn() -> (Box<dyn Fn()->Box<i32>>) + Sync) = &|| { +static FN : &'static (dyn Fn() -> Box<dyn Fn()->Box<i32>> + Sync) = &|| { let x = Box::new(0); Box::new(|| x) //~ ERROR cannot move out of `x`, a captured variable in an `Fn` closure }; diff --git a/tests/ui/borrowck/borrowck-in-static.stderr b/tests/ui/borrowck/borrowck-in-static.stderr index 8171e6950ac..745b02ae21b 100644 --- a/tests/ui/borrowck/borrowck-in-static.stderr +++ b/tests/ui/borrowck/borrowck-in-static.stderr @@ -7,6 +7,11 @@ LL | Box::new(|| x) | -- ^ move occurs because `x` has type `Box<i32>`, which does not implement the `Copy` trait | | | captured by this `Fn` closure + | +help: consider cloning the value if the performance cost is acceptable + | +LL | Box::new(|| x.clone()) + | ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-init-in-called-fn-expr.stderr b/tests/ui/borrowck/borrowck-init-in-called-fn-expr.stderr index a27b6956b30..bfe3c60a84a 100644 --- a/tests/ui/borrowck/borrowck-init-in-called-fn-expr.stderr +++ b/tests/ui/borrowck/borrowck-init-in-called-fn-expr.stderr @@ -8,8 +8,8 @@ LL | i | help: consider assigning a value | -LL | let i: isize = 0; - | +++ +LL | let i: isize = 42; + | ++++ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-init-in-fn-expr.stderr b/tests/ui/borrowck/borrowck-init-in-fn-expr.stderr index 16f4c40f529..a248a6d85b6 100644 --- a/tests/ui/borrowck/borrowck-init-in-fn-expr.stderr +++ b/tests/ui/borrowck/borrowck-init-in-fn-expr.stderr @@ -8,8 +8,8 @@ LL | i | help: consider assigning a value | -LL | let i: isize = 0; - | +++ +LL | let i: isize = 42; + | ++++ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-init-in-fru.stderr b/tests/ui/borrowck/borrowck-init-in-fru.stderr index f27993e10b4..b5c332a90bc 100644 --- a/tests/ui/borrowck/borrowck-init-in-fru.stderr +++ b/tests/ui/borrowck/borrowck-init-in-fru.stderr @@ -8,8 +8,8 @@ LL | origin = Point { x: 10, ..origin }; | help: consider assigning a value | -LL | let mut origin: Point = todo!(); - | +++++++++ +LL | let mut origin: Point = value; + | +++++++ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-init-op-equal.stderr b/tests/ui/borrowck/borrowck-init-op-equal.stderr index 241d24341cb..d621c4ab46e 100644 --- a/tests/ui/borrowck/borrowck-init-op-equal.stderr +++ b/tests/ui/borrowck/borrowck-init-op-equal.stderr @@ -8,8 +8,8 @@ LL | v += 1; | help: consider assigning a value | -LL | let v: isize = 0; - | +++ +LL | let v: isize = 42; + | ++++ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-init-plus-equal.stderr b/tests/ui/borrowck/borrowck-init-plus-equal.stderr index 65de6e8bf5d..109321386ba 100644 --- a/tests/ui/borrowck/borrowck-init-plus-equal.stderr +++ b/tests/ui/borrowck/borrowck-init-plus-equal.stderr @@ -8,8 +8,8 @@ LL | v = v + 1; | help: consider assigning a value | -LL | let mut v: isize = 0; - | +++ +LL | let mut v: isize = 42; + | ++++ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-issue-2657-2.fixed b/tests/ui/borrowck/borrowck-issue-2657-2.fixed index e5aaf7d2de7..e532aa3e68c 100644 --- a/tests/ui/borrowck/borrowck-issue-2657-2.fixed +++ b/tests/ui/borrowck/borrowck-issue-2657-2.fixed @@ -5,7 +5,7 @@ fn main() { match x { Some(ref y) => { - let _b = y; //~ ERROR cannot move out + let _b = y.clone(); //~ ERROR cannot move out } _ => {} } diff --git a/tests/ui/borrowck/borrowck-issue-2657-2.stderr b/tests/ui/borrowck/borrowck-issue-2657-2.stderr index 6fab19000fc..16186792b93 100644 --- a/tests/ui/borrowck/borrowck-issue-2657-2.stderr +++ b/tests/ui/borrowck/borrowck-issue-2657-2.stderr @@ -9,6 +9,11 @@ help: consider removing the dereference here LL - let _b = *y; LL + let _b = y; | +help: consider cloning the value if the performance cost is acceptable + | +LL - let _b = *y; +LL + let _b = y.clone(); + | error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-issue-48962.stderr b/tests/ui/borrowck/borrowck-issue-48962.stderr index ee174f6736e..6e821a4c6b0 100644 --- a/tests/ui/borrowck/borrowck-issue-48962.stderr +++ b/tests/ui/borrowck/borrowck-issue-48962.stderr @@ -17,6 +17,11 @@ LL | {src}; | --- value moved here LL | src.0 = 66; | ^^^^^^^^^^ value used here after move + | +help: consider cloning the value if the performance cost is acceptable + | +LL | {src.clone()}; + | ++++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/borrowck/borrowck-loan-blocks-move-cc.stderr b/tests/ui/borrowck/borrowck-loan-blocks-move-cc.stderr index 86479043a06..370ae058f44 100644 --- a/tests/ui/borrowck/borrowck-loan-blocks-move-cc.stderr +++ b/tests/ui/borrowck/borrowck-loan-blocks-move-cc.stderr @@ -13,6 +13,12 @@ LL | println!("v={}", *v); LL | }); LL | w.use_ref(); | - borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL - let w = &v; +LL + let w = v.clone(); + | error[E0505]: cannot move out of `v` because it is borrowed --> $DIR/borrowck-loan-blocks-move-cc.rs:24:19 @@ -29,6 +35,12 @@ LL | println!("v={}", *v); LL | }); LL | w.use_ref(); | - borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL - let w = &v; +LL + let w = v.clone(); + | error: aborting due to 2 previous errors diff --git a/tests/ui/borrowck/borrowck-loan-blocks-move.stderr b/tests/ui/borrowck/borrowck-loan-blocks-move.stderr index d1fbc5b47db..8a8005dbb83 100644 --- a/tests/ui/borrowck/borrowck-loan-blocks-move.stderr +++ b/tests/ui/borrowck/borrowck-loan-blocks-move.stderr @@ -9,6 +9,12 @@ LL | take(v); | ^ move out of `v` occurs here LL | w.use_ref(); | - borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL - let w = &v; +LL + let w = v.clone(); + | error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-move-from-subpath-of-borrowed-path.stderr b/tests/ui/borrowck/borrowck-move-from-subpath-of-borrowed-path.stderr index a41c4af98e7..a23a203d999 100644 --- a/tests/ui/borrowck/borrowck-move-from-subpath-of-borrowed-path.stderr +++ b/tests/ui/borrowck/borrowck-move-from-subpath-of-borrowed-path.stderr @@ -10,6 +10,12 @@ LL | let z = *a; | ^^ move out of `*a` occurs here LL | b.use_ref(); | - borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL - let b = &a; +LL + let b = a.clone(); + | error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-move-from-unsafe-ptr.stderr b/tests/ui/borrowck/borrowck-move-from-unsafe-ptr.stderr index 7213f85ad98..ebc3b6ebcac 100644 --- a/tests/ui/borrowck/borrowck-move-from-unsafe-ptr.stderr +++ b/tests/ui/borrowck/borrowck-move-from-unsafe-ptr.stderr @@ -9,6 +9,11 @@ help: consider removing the dereference here LL - let y = *x; LL + let y = x; | +help: consider cloning the value if the performance cost is acceptable + | +LL - let y = *x; +LL + let y = x.clone(); + | error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-move-mut-base-ptr.stderr b/tests/ui/borrowck/borrowck-move-mut-base-ptr.stderr index 88eb6c8ceee..acf426906c3 100644 --- a/tests/ui/borrowck/borrowck-move-mut-base-ptr.stderr +++ b/tests/ui/borrowck/borrowck-move-mut-base-ptr.stderr @@ -10,6 +10,12 @@ LL | let t1 = t0; LL | *t1 = 22; LL | p.use_ref(); | - borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL - let p: &isize = &*t0; // Freezes `*t0` +LL + let p: &isize = t0.clone(); // Freezes `*t0` + | error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.fixed b/tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.fixed index 8d5ebbc7744..a19db7e5cd3 100644 --- a/tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.fixed +++ b/tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.fixed @@ -2,6 +2,6 @@ use std::rc::Rc; pub fn main() { - let _x = <Vec<i32> as Clone>::clone(&Rc::new(vec![1, 2])).into_iter(); + let _x = <Vec<i32> as Clone>::clone(&Rc::new(vec![1, 2]).clone()).into_iter(); //~^ ERROR [E0507] } diff --git a/tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.stderr b/tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.stderr index 076f0ce3440..577c2de38be 100644 --- a/tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.stderr +++ b/tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.stderr @@ -12,6 +12,10 @@ help: you can `clone` the value and consume it, but this might not be your desir | LL | let _x = <Vec<i32> as Clone>::clone(&Rc::new(vec![1, 2])).into_iter(); | ++++++++++++++++++++++++++++ + +help: consider cloning the value if the performance cost is acceptable + | +LL | let _x = Rc::new(vec![1, 2]).clone().into_iter(); + | ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-move-out-of-overloaded-deref.stderr b/tests/ui/borrowck/borrowck-move-out-of-overloaded-deref.stderr index dce1f4d0775..774aac3ec9c 100644 --- a/tests/ui/borrowck/borrowck-move-out-of-overloaded-deref.stderr +++ b/tests/ui/borrowck/borrowck-move-out-of-overloaded-deref.stderr @@ -9,6 +9,11 @@ help: consider removing the dereference here LL - let _x = *Rc::new("hi".to_string()); LL + let _x = Rc::new("hi".to_string()); | +help: consider cloning the value if the performance cost is acceptable + | +LL - let _x = *Rc::new("hi".to_string()); +LL + let _x = Rc::new("hi".to_string()).clone(); + | error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-move-out-of-static-item.rs b/tests/ui/borrowck/borrowck-move-out-of-static-item.rs index d01fb261894..b24d9b932cd 100644 --- a/tests/ui/borrowck/borrowck-move-out-of-static-item.rs +++ b/tests/ui/borrowck/borrowck-move-out-of-static-item.rs @@ -8,7 +8,8 @@ static BAR: Foo = Foo { foo: 5 }; fn test(f: Foo) { - let _f = Foo{foo: 4, ..f}; + let f = Foo { foo: 4, ..f }; + println!("{}", f.foo); } fn main() { diff --git a/tests/ui/borrowck/borrowck-move-out-of-static-item.stderr b/tests/ui/borrowck/borrowck-move-out-of-static-item.stderr index 07dcaf875e7..86bddacbdc7 100644 --- a/tests/ui/borrowck/borrowck-move-out-of-static-item.stderr +++ b/tests/ui/borrowck/borrowck-move-out-of-static-item.stderr @@ -1,8 +1,14 @@ error[E0507]: cannot move out of static item `BAR` - --> $DIR/borrowck-move-out-of-static-item.rs:15:10 + --> $DIR/borrowck-move-out-of-static-item.rs:16:10 | LL | test(BAR); | ^^^ move occurs because `BAR` has type `Foo`, which does not implement the `Copy` trait + | +note: if `Foo` implemented `Clone`, you could clone the value + --> $DIR/borrowck-move-out-of-static-item.rs:3:1 + | +LL | struct Foo { + | ^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-move-subcomponent.stderr b/tests/ui/borrowck/borrowck-move-subcomponent.stderr index 8408d99156a..4d9477f8581 100644 --- a/tests/ui/borrowck/borrowck-move-subcomponent.stderr +++ b/tests/ui/borrowck/borrowck-move-subcomponent.stderr @@ -9,6 +9,12 @@ LL | let S { x: ax } = a; | ^^ move out of `a.x` occurs here LL | f(pb); | -- borrow later used here + | +note: if `S` implemented `Clone`, you could clone the value + --> $DIR/borrowck-move-subcomponent.rs:6:1 + | +LL | struct S { + | ^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-multiple-captures.stderr b/tests/ui/borrowck/borrowck-multiple-captures.stderr index 70abe7b346e..fdac4c27cee 100644 --- a/tests/ui/borrowck/borrowck-multiple-captures.stderr +++ b/tests/ui/borrowck/borrowck-multiple-captures.stderr @@ -14,6 +14,12 @@ LL | drop(x1); ... LL | borrow(&*p1); | ---- borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL - let p1 = &x1; +LL + let p1 = x1.clone(); + | error[E0505]: cannot move out of `x2` because it is borrowed --> $DIR/borrowck-multiple-captures.rs:12:19 @@ -30,6 +36,12 @@ LL | drop(x2); ... LL | borrow(&*p2); | ---- borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL - let p2 = &x2; +LL + let p2 = x2.clone(); + | error[E0382]: use of moved value: `x1` --> $DIR/borrowck-multiple-captures.rs:27:19 @@ -93,6 +105,12 @@ LL | drop(x); ... LL | borrow(&*p); | --- borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL - let p = &x; +LL + let p = x.clone(); + | error[E0382]: use of moved value: `x` --> $DIR/borrowck-multiple-captures.rs:52:14 diff --git a/tests/ui/borrowck/borrowck-overloaded-call.stderr b/tests/ui/borrowck/borrowck-overloaded-call.stderr index 723b19f4124..1602058c183 100644 --- a/tests/ui/borrowck/borrowck-overloaded-call.stderr +++ b/tests/ui/borrowck/borrowck-overloaded-call.stderr @@ -29,6 +29,12 @@ LL | s(" world".to_string()); | - value moved here LL | s(" world".to_string()); | ^ value used here after move + | +note: if `SFnOnce` implemented `Clone`, you could clone the value + --> $DIR/borrowck-overloaded-call.rs:41:1 + | +LL | struct SFnOnce { + | ^^^^^^^^^^^^^^ error: aborting due to 3 previous errors diff --git a/tests/ui/borrowck/borrowck-overloaded-index-move-from-vec.stderr b/tests/ui/borrowck/borrowck-overloaded-index-move-from-vec.stderr index b4106702cd1..3e874ed1a2f 100644 --- a/tests/ui/borrowck/borrowck-overloaded-index-move-from-vec.stderr +++ b/tests/ui/borrowck/borrowck-overloaded-index-move-from-vec.stderr @@ -8,6 +8,10 @@ help: consider borrowing here | LL | let bad = &v[0]; | + +help: consider cloning the value if the performance cost is acceptable + | +LL | let bad = v[0].clone(); + | ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-return.stderr b/tests/ui/borrowck/borrowck-return.stderr index a1bc3008ea8..f680b9af4f0 100644 --- a/tests/ui/borrowck/borrowck-return.stderr +++ b/tests/ui/borrowck/borrowck-return.stderr @@ -8,8 +8,8 @@ LL | return x; | help: consider assigning a value | -LL | let x: isize = 0; - | +++ +LL | let x: isize = 42; + | ++++ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-storage-dead.stderr b/tests/ui/borrowck/borrowck-storage-dead.stderr index a08e2a7b535..5f29c61c7eb 100644 --- a/tests/ui/borrowck/borrowck-storage-dead.stderr +++ b/tests/ui/borrowck/borrowck-storage-dead.stderr @@ -8,8 +8,8 @@ LL | let _ = x + 1; | help: consider assigning a value | -LL | let x: i32 = 0; - | +++ +LL | let x: i32 = 42; + | ++++ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-struct-update-with-dtor.rs b/tests/ui/borrowck/borrowck-struct-update-with-dtor.rs index 1f6ed6d46aa..f0d067477c6 100644 --- a/tests/ui/borrowck/borrowck-struct-update-with-dtor.rs +++ b/tests/ui/borrowck/borrowck-struct-update-with-dtor.rs @@ -2,20 +2,63 @@ // move, when the struct implements Drop. struct B; -struct S { a: isize, b: B } -impl Drop for S { fn drop(&mut self) { } } +struct S<K> { a: isize, b: B, c: K } +impl<K> Drop for S<K> { fn drop(&mut self) { } } -struct T { a: isize, mv: Box<isize> } +struct T { a: isize, b: Box<isize> } impl Drop for T { fn drop(&mut self) { } } -fn f(s0:S) { - let _s2 = S{a: 2, ..s0}; - //~^ ERROR [E0509] +struct V<K> { a: isize, b: Box<isize>, c: K } +impl<K> Drop for V<K> { fn drop(&mut self) { } } + +#[derive(Clone)] +struct Clonable; + +mod not_all_clone { + use super::*; + fn a(s0: S<()>) { + let _s2 = S { a: 2, ..s0 }; + //~^ ERROR [E0509] + } + fn b(s0: S<B>) { + let _s2 = S { a: 2, ..s0 }; + //~^ ERROR [E0509] + //~| ERROR [E0509] + } + fn c<K: Clone>(s0: S<K>) { + let _s2 = S { a: 2, ..s0 }; + //~^ ERROR [E0509] + //~| ERROR [E0509] + } } +mod all_clone { + use super::*; + fn a(s0: T) { + let _s2 = T { a: 2, ..s0 }; + //~^ ERROR [E0509] + } + + fn b(s0: T) { + let _s2 = T { ..s0 }; + //~^ ERROR [E0509] + } + + fn c(s0: T) { + let _s2 = T { a: 2, b: s0.b }; + //~^ ERROR [E0509] + } + + fn d<K: Clone>(s0: V<K>) { + let _s2 = V { a: 2, ..s0 }; + //~^ ERROR [E0509] + //~| ERROR [E0509] + } -fn g(s0:T) { - let _s2 = T{a: 2, ..s0}; - //~^ ERROR [E0509] + fn e(s0: V<Clonable>) { + let _s2 = V { a: 2, ..s0 }; + //~^ ERROR [E0509] + //~| ERROR [E0509] + } } fn main() { } diff --git a/tests/ui/borrowck/borrowck-struct-update-with-dtor.stderr b/tests/ui/borrowck/borrowck-struct-update-with-dtor.stderr index af32f279100..bc11204acf2 100644 --- a/tests/ui/borrowck/borrowck-struct-update-with-dtor.stderr +++ b/tests/ui/borrowck/borrowck-struct-update-with-dtor.stderr @@ -1,21 +1,191 @@ -error[E0509]: cannot move out of type `S`, which implements the `Drop` trait - --> $DIR/borrowck-struct-update-with-dtor.rs:12:15 +error[E0509]: cannot move out of type `S<()>`, which implements the `Drop` trait + --> $DIR/borrowck-struct-update-with-dtor.rs:20:19 | -LL | let _s2 = S{a: 2, ..s0}; - | ^^^^^^^^^^^^^ - | | - | cannot move out of here - | move occurs because `s0.b` has type `B`, which does not implement the `Copy` trait +LL | let _s2 = S { a: 2, ..s0 }; + | ^^^^^^^^^^^^^^^^ + | | + | cannot move out of here + | move occurs because `s0.b` has type `B`, which does not implement the `Copy` trait + | +note: `B` doesn't implement `Copy` or `Clone` + --> $DIR/borrowck-struct-update-with-dtor.rs:4:1 + | +LL | struct B; + | ^^^^^^^^ +help: if `B` implemented `Clone`, you could clone the value from the field instead of using the functional record update syntax + | +LL | let _s2 = S { a: 2, b: s0.b.clone(), ..s0 }; + | +++++++++++++++++ + +error[E0509]: cannot move out of type `S<B>`, which implements the `Drop` trait + --> $DIR/borrowck-struct-update-with-dtor.rs:24:19 + | +LL | let _s2 = S { a: 2, ..s0 }; + | ^^^^^^^^^^^^^^^^ + | | + | cannot move out of here + | move occurs because `s0.b` has type `B`, which does not implement the `Copy` trait + | +note: `B` doesn't implement `Copy` or `Clone` + --> $DIR/borrowck-struct-update-with-dtor.rs:4:1 + | +LL | struct B; + | ^^^^^^^^ +help: if `B` implemented `Clone`, you could clone the value from the field instead of using the functional record update syntax + | +LL | let _s2 = S { a: 2, b: s0.b.clone(), c: s0.c.clone() }; + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error[E0509]: cannot move out of type `S<B>`, which implements the `Drop` trait + --> $DIR/borrowck-struct-update-with-dtor.rs:24:19 + | +LL | let _s2 = S { a: 2, ..s0 }; + | ^^^^^^^^^^^^^^^^ + | | + | cannot move out of here + | move occurs because `s0.c` has type `B`, which does not implement the `Copy` trait + | +note: `B` doesn't implement `Copy` or `Clone` + --> $DIR/borrowck-struct-update-with-dtor.rs:4:1 + | +LL | struct B; + | ^^^^^^^^ +help: if `B` implemented `Clone`, you could clone the value from the field instead of using the functional record update syntax + | +LL | let _s2 = S { a: 2, b: s0.b.clone(), c: s0.c.clone() }; + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error[E0509]: cannot move out of type `S<K>`, which implements the `Drop` trait + --> $DIR/borrowck-struct-update-with-dtor.rs:29:19 + | +LL | let _s2 = S { a: 2, ..s0 }; + | ^^^^^^^^^^^^^^^^ + | | + | cannot move out of here + | move occurs because `s0.b` has type `B`, which does not implement the `Copy` trait + | +note: `B` doesn't implement `Copy` or `Clone` + --> $DIR/borrowck-struct-update-with-dtor.rs:4:1 + | +LL | struct B; + | ^^^^^^^^ +help: if `B` implemented `Clone`, you could clone the value from the field instead of using the functional record update syntax + | +LL | let _s2 = S { a: 2, b: s0.b.clone(), ..s0 }; + | +++++++++++++++++ + +error[E0509]: cannot move out of type `S<K>`, which implements the `Drop` trait + --> $DIR/borrowck-struct-update-with-dtor.rs:29:19 + | +LL | let _s2 = S { a: 2, ..s0 }; + | ^^^^^^^^^^^^^^^^ + | | + | cannot move out of here + | move occurs because `s0.c` has type `K`, which does not implement the `Copy` trait + | +help: clone the value from the field instead of using the functional record update syntax + | +LL | let _s2 = S { a: 2, c: s0.c.clone(), ..s0 }; + | +++++++++++++++++ + +error[E0509]: cannot move out of type `T`, which implements the `Drop` trait + --> $DIR/borrowck-struct-update-with-dtor.rs:37:19 + | +LL | let _s2 = T { a: 2, ..s0 }; + | ^^^^^^^^^^^^^^^^ + | | + | cannot move out of here + | move occurs because `s0.b` has type `Box<isize>`, which does not implement the `Copy` trait + | +help: clone the value from the field instead of using the functional record update syntax + | +LL | let _s2 = T { a: 2, b: s0.b.clone() }; + | ~~~~~~~~~~~~~~~~~ + +error[E0509]: cannot move out of type `T`, which implements the `Drop` trait + --> $DIR/borrowck-struct-update-with-dtor.rs:42:19 + | +LL | let _s2 = T { ..s0 }; + | ^^^^^^^^^^ + | | + | cannot move out of here + | move occurs because `s0.b` has type `Box<isize>`, which does not implement the `Copy` trait + | +help: clone the value from the field instead of using the functional record update syntax + | +LL | let _s2 = T { b: s0.b.clone(), ..s0 }; + | ~~~~~~~~~~~~~~~~~~~~~~~~~ error[E0509]: cannot move out of type `T`, which implements the `Drop` trait - --> $DIR/borrowck-struct-update-with-dtor.rs:17:15 + --> $DIR/borrowck-struct-update-with-dtor.rs:47:32 + | +LL | let _s2 = T { a: 2, b: s0.b }; + | ^^^^ + | | + | cannot move out of here + | move occurs because `s0.b` has type `Box<isize>`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let _s2 = T { a: 2, b: s0.b.clone() }; + | ++++++++ + +error[E0509]: cannot move out of type `V<K>`, which implements the `Drop` trait + --> $DIR/borrowck-struct-update-with-dtor.rs:52:19 + | +LL | let _s2 = V { a: 2, ..s0 }; + | ^^^^^^^^^^^^^^^^ + | | + | cannot move out of here + | move occurs because `s0.b` has type `Box<isize>`, which does not implement the `Copy` trait + | +help: clone the value from the field instead of using the functional record update syntax + | +LL | let _s2 = V { a: 2, b: s0.b.clone(), ..s0 }; + | +++++++++++++++++ + +error[E0509]: cannot move out of type `V<K>`, which implements the `Drop` trait + --> $DIR/borrowck-struct-update-with-dtor.rs:52:19 + | +LL | let _s2 = V { a: 2, ..s0 }; + | ^^^^^^^^^^^^^^^^ + | | + | cannot move out of here + | move occurs because `s0.c` has type `K`, which does not implement the `Copy` trait + | +help: clone the value from the field instead of using the functional record update syntax + | +LL | let _s2 = V { a: 2, c: s0.c.clone(), ..s0 }; + | +++++++++++++++++ + +error[E0509]: cannot move out of type `V<Clonable>`, which implements the `Drop` trait + --> $DIR/borrowck-struct-update-with-dtor.rs:58:19 + | +LL | let _s2 = V { a: 2, ..s0 }; + | ^^^^^^^^^^^^^^^^ + | | + | cannot move out of here + | move occurs because `s0.b` has type `Box<isize>`, which does not implement the `Copy` trait + | +help: clone the value from the field instead of using the functional record update syntax + | +LL | let _s2 = V { a: 2, b: s0.b.clone(), ..s0 }; + | +++++++++++++++++ + +error[E0509]: cannot move out of type `V<Clonable>`, which implements the `Drop` trait + --> $DIR/borrowck-struct-update-with-dtor.rs:58:19 + | +LL | let _s2 = V { a: 2, ..s0 }; + | ^^^^^^^^^^^^^^^^ + | | + | cannot move out of here + | move occurs because `s0.c` has type `Clonable`, which does not implement the `Copy` trait + | +help: clone the value from the field instead of using the functional record update syntax | -LL | let _s2 = T{a: 2, ..s0}; - | ^^^^^^^^^^^^^ - | | - | cannot move out of here - | move occurs because `s0.mv` has type `Box<isize>`, which does not implement the `Copy` trait +LL | let _s2 = V { a: 2, c: s0.c.clone(), ..s0 }; + | +++++++++++++++++ -error: aborting due to 2 previous errors +error: aborting due to 12 previous errors For more information about this error, try `rustc --explain E0509`. diff --git a/tests/ui/borrowck/borrowck-unary-move.stderr b/tests/ui/borrowck/borrowck-unary-move.stderr index e6c3869f67a..598ecb53778 100644 --- a/tests/ui/borrowck/borrowck-unary-move.stderr +++ b/tests/ui/borrowck/borrowck-unary-move.stderr @@ -9,6 +9,12 @@ LL | free(x); | ^ move out of `x` occurs here LL | *y | -- borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL - let y = &*x; +LL + let y = x.clone(); + | error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-unboxed-closures.stderr b/tests/ui/borrowck/borrowck-unboxed-closures.stderr index 3634676463c..a4513bd614e 100644 --- a/tests/ui/borrowck/borrowck-unboxed-closures.stderr +++ b/tests/ui/borrowck/borrowck-unboxed-closures.stderr @@ -29,15 +29,13 @@ LL | f(1, 2); LL | f(1, 2); | ^ value used here after move | -note: this value implements `FnOnce`, which causes it to be moved when called - --> $DIR/borrowck-unboxed-closures.rs:11:5 +note: `FnOnce` closures can only be called once + --> $DIR/borrowck-unboxed-closures.rs:10:8 | +LL | fn c<F:FnOnce(isize, isize) -> isize>(f: F) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `F` is made to be an `FnOnce` closure here LL | f(1, 2); - | ^ -help: consider further restricting this bound - | -LL | fn c<F:FnOnce(isize, isize) -> isize + Copy>(f: F) { - | ++++++ + | ------- this value implements `FnOnce`, which causes it to be moved when called error: aborting due to 3 previous errors diff --git a/tests/ui/borrowck/borrowck-uninit-after-item.stderr b/tests/ui/borrowck/borrowck-uninit-after-item.stderr index 06bb419aa3b..c6d52a049d2 100644 --- a/tests/ui/borrowck/borrowck-uninit-after-item.stderr +++ b/tests/ui/borrowck/borrowck-uninit-after-item.stderr @@ -9,8 +9,8 @@ LL | baz(bar); | help: consider assigning a value | -LL | let bar = 0; - | +++ +LL | let bar = 42; + | ++++ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-uninit-in-assignop.stderr b/tests/ui/borrowck/borrowck-uninit-in-assignop.stderr index fdbb451bde4..aaa33f08ff5 100644 --- a/tests/ui/borrowck/borrowck-uninit-in-assignop.stderr +++ b/tests/ui/borrowck/borrowck-uninit-in-assignop.stderr @@ -8,8 +8,8 @@ LL | x += 1; | help: consider assigning a value | -LL | let x: isize = 0; - | +++ +LL | let x: isize = 42; + | ++++ error[E0381]: used binding `x` isn't initialized --> $DIR/borrowck-uninit-in-assignop.rs:9:5 @@ -21,8 +21,8 @@ LL | x -= 1; | help: consider assigning a value | -LL | let x: isize = 0; - | +++ +LL | let x: isize = 42; + | ++++ error[E0381]: used binding `x` isn't initialized --> $DIR/borrowck-uninit-in-assignop.rs:12:5 @@ -34,8 +34,8 @@ LL | x *= 1; | help: consider assigning a value | -LL | let x: isize = 0; - | +++ +LL | let x: isize = 42; + | ++++ error[E0381]: used binding `x` isn't initialized --> $DIR/borrowck-uninit-in-assignop.rs:15:5 @@ -47,8 +47,8 @@ LL | x /= 1; | help: consider assigning a value | -LL | let x: isize = 0; - | +++ +LL | let x: isize = 42; + | ++++ error[E0381]: used binding `x` isn't initialized --> $DIR/borrowck-uninit-in-assignop.rs:18:5 @@ -60,8 +60,8 @@ LL | x %= 1; | help: consider assigning a value | -LL | let x: isize = 0; - | +++ +LL | let x: isize = 42; + | ++++ error[E0381]: used binding `x` isn't initialized --> $DIR/borrowck-uninit-in-assignop.rs:21:5 @@ -73,8 +73,8 @@ LL | x ^= 1; | help: consider assigning a value | -LL | let x: isize = 0; - | +++ +LL | let x: isize = 42; + | ++++ error[E0381]: used binding `x` isn't initialized --> $DIR/borrowck-uninit-in-assignop.rs:24:5 @@ -86,8 +86,8 @@ LL | x &= 1; | help: consider assigning a value | -LL | let x: isize = 0; - | +++ +LL | let x: isize = 42; + | ++++ error[E0381]: used binding `x` isn't initialized --> $DIR/borrowck-uninit-in-assignop.rs:27:5 @@ -99,8 +99,8 @@ LL | x |= 1; | help: consider assigning a value | -LL | let x: isize = 0; - | +++ +LL | let x: isize = 42; + | ++++ error[E0381]: used binding `x` isn't initialized --> $DIR/borrowck-uninit-in-assignop.rs:30:5 @@ -112,8 +112,8 @@ LL | x <<= 1; | help: consider assigning a value | -LL | let x: isize = 0; - | +++ +LL | let x: isize = 42; + | ++++ error[E0381]: used binding `x` isn't initialized --> $DIR/borrowck-uninit-in-assignop.rs:33:5 @@ -125,8 +125,8 @@ LL | x >>= 1; | help: consider assigning a value | -LL | let x: isize = 0; - | +++ +LL | let x: isize = 42; + | ++++ error: aborting due to 10 previous errors diff --git a/tests/ui/borrowck/borrowck-uninit-ref-chain.stderr b/tests/ui/borrowck/borrowck-uninit-ref-chain.stderr index 73fded7545c..d6759b8e1cf 100644 --- a/tests/ui/borrowck/borrowck-uninit-ref-chain.stderr +++ b/tests/ui/borrowck/borrowck-uninit-ref-chain.stderr @@ -8,8 +8,8 @@ LL | let _y = &**x; | help: consider assigning a value | -LL | let x: &&Box<i32> = todo!(); - | +++++++++ +LL | let x: &&Box<i32> = &&Box::new(42); + | ++++++++++++++++ error[E0381]: used binding `x` isn't initialized --> $DIR/borrowck-uninit-ref-chain.rs:11:14 @@ -21,7 +21,7 @@ LL | let _y = &**x; | help: consider assigning a value | -LL | let x: &&S<i32, i32> = todo!(); +LL | let x: &&S<i32, i32> = &&value; | +++++++++ error[E0381]: used binding `x` isn't initialized @@ -34,8 +34,8 @@ LL | let _y = &**x; | help: consider assigning a value | -LL | let x: &&i32 = todo!(); - | +++++++++ +LL | let x: &&i32 = &&42; + | ++++++ error[E0381]: partially assigned binding `a` isn't fully initialized --> $DIR/borrowck-uninit-ref-chain.rs:18:5 diff --git a/tests/ui/borrowck/borrowck-uninit.stderr b/tests/ui/borrowck/borrowck-uninit.stderr index 1e004baa143..9538baeafd1 100644 --- a/tests/ui/borrowck/borrowck-uninit.stderr +++ b/tests/ui/borrowck/borrowck-uninit.stderr @@ -8,8 +8,8 @@ LL | foo(x); | help: consider assigning a value | -LL | let x: isize = 0; - | +++ +LL | let x: isize = 42; + | ++++ error[E0381]: used binding `a` isn't initialized --> $DIR/borrowck-uninit.rs:14:32 diff --git a/tests/ui/borrowck/borrowck-use-in-index-lvalue.fixed b/tests/ui/borrowck/borrowck-use-in-index-lvalue.fixed new file mode 100644 index 00000000000..947c7f5b744 --- /dev/null +++ b/tests/ui/borrowck/borrowck-use-in-index-lvalue.fixed @@ -0,0 +1,11 @@ +//@ run-rustfix +#[allow(unused_mut)] +fn test() { + let w: &mut [isize] = &mut []; + w[5] = 0; //~ ERROR [E0381] + + let mut w: &mut [isize] = &mut []; + w[5] = 0; //~ ERROR [E0381] +} + +fn main() { test(); } diff --git a/tests/ui/borrowck/borrowck-use-in-index-lvalue.rs b/tests/ui/borrowck/borrowck-use-in-index-lvalue.rs index d30b1de5cd0..a00fda60abb 100644 --- a/tests/ui/borrowck/borrowck-use-in-index-lvalue.rs +++ b/tests/ui/borrowck/borrowck-use-in-index-lvalue.rs @@ -1,3 +1,5 @@ +//@ run-rustfix +#[allow(unused_mut)] fn test() { let w: &mut [isize]; w[5] = 0; //~ ERROR [E0381] diff --git a/tests/ui/borrowck/borrowck-use-in-index-lvalue.stderr b/tests/ui/borrowck/borrowck-use-in-index-lvalue.stderr index 18e808f10d0..6ec4390ae8d 100644 --- a/tests/ui/borrowck/borrowck-use-in-index-lvalue.stderr +++ b/tests/ui/borrowck/borrowck-use-in-index-lvalue.stderr @@ -1,5 +1,5 @@ error[E0381]: used binding `w` isn't initialized - --> $DIR/borrowck-use-in-index-lvalue.rs:3:5 + --> $DIR/borrowck-use-in-index-lvalue.rs:5:5 | LL | let w: &mut [isize]; | - binding declared here but left uninitialized @@ -8,11 +8,11 @@ LL | w[5] = 0; | help: consider assigning a value | -LL | let w: &mut [isize] = todo!(); +LL | let w: &mut [isize] = &mut []; | +++++++++ error[E0381]: used binding `w` isn't initialized - --> $DIR/borrowck-use-in-index-lvalue.rs:6:5 + --> $DIR/borrowck-use-in-index-lvalue.rs:8:5 | LL | let mut w: &mut [isize]; | ----- binding declared here but left uninitialized @@ -21,7 +21,7 @@ LL | w[5] = 0; | help: consider assigning a value | -LL | let mut w: &mut [isize] = todo!(); +LL | let mut w: &mut [isize] = &mut []; | +++++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/borrowck/borrowck-use-uninitialized-in-cast-trait.fixed b/tests/ui/borrowck/borrowck-use-uninitialized-in-cast-trait.fixed new file mode 100644 index 00000000000..f6ea5f0b6b8 --- /dev/null +++ b/tests/ui/borrowck/borrowck-use-uninitialized-in-cast-trait.fixed @@ -0,0 +1,12 @@ +// Variation on `borrowck-use-uninitialized-in-cast` in which we do a +// trait cast from an uninitialized source. Issue #20791. +//@ run-rustfix +#![allow(unused_variables, dead_code)] + +trait Foo { fn dummy(&self) { } } +impl Foo for i32 { } + +fn main() { + let x: &i32 = &42; + let y = x as *const dyn Foo; //~ ERROR [E0381] +} diff --git a/tests/ui/borrowck/borrowck-use-uninitialized-in-cast-trait.rs b/tests/ui/borrowck/borrowck-use-uninitialized-in-cast-trait.rs index 3ce72161814..a384fdbf950 100644 --- a/tests/ui/borrowck/borrowck-use-uninitialized-in-cast-trait.rs +++ b/tests/ui/borrowck/borrowck-use-uninitialized-in-cast-trait.rs @@ -1,5 +1,7 @@ // Variation on `borrowck-use-uninitialized-in-cast` in which we do a // trait cast from an uninitialized source. Issue #20791. +//@ run-rustfix +#![allow(unused_variables, dead_code)] trait Foo { fn dummy(&self) { } } impl Foo for i32 { } diff --git a/tests/ui/borrowck/borrowck-use-uninitialized-in-cast-trait.stderr b/tests/ui/borrowck/borrowck-use-uninitialized-in-cast-trait.stderr index dcbaa75333e..ef04979f1cd 100644 --- a/tests/ui/borrowck/borrowck-use-uninitialized-in-cast-trait.stderr +++ b/tests/ui/borrowck/borrowck-use-uninitialized-in-cast-trait.stderr @@ -1,5 +1,5 @@ error[E0381]: used binding `x` isn't initialized - --> $DIR/borrowck-use-uninitialized-in-cast-trait.rs:9:13 + --> $DIR/borrowck-use-uninitialized-in-cast-trait.rs:11:13 | LL | let x: &i32; | - binding declared here but left uninitialized @@ -8,8 +8,8 @@ LL | let y = x as *const dyn Foo; | help: consider assigning a value | -LL | let x: &i32 = todo!(); - | +++++++++ +LL | let x: &i32 = &42; + | +++++ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-use-uninitialized-in-cast.fixed b/tests/ui/borrowck/borrowck-use-uninitialized-in-cast.fixed new file mode 100644 index 00000000000..9c72015d747 --- /dev/null +++ b/tests/ui/borrowck/borrowck-use-uninitialized-in-cast.fixed @@ -0,0 +1,10 @@ +// Check that we detect unused values that are cast to other things. +// The problem was specified to casting to `*`, as creating unsafe +// pointers was not being fully checked. Issue #20791. +//@ run-rustfix +#![allow(unused_variables)] + +fn main() { + let x: &i32 = &42; + let y = x as *const i32; //~ ERROR [E0381] +} diff --git a/tests/ui/borrowck/borrowck-use-uninitialized-in-cast.rs b/tests/ui/borrowck/borrowck-use-uninitialized-in-cast.rs index a355a546dc6..290deb0f257 100644 --- a/tests/ui/borrowck/borrowck-use-uninitialized-in-cast.rs +++ b/tests/ui/borrowck/borrowck-use-uninitialized-in-cast.rs @@ -1,6 +1,8 @@ // Check that we detect unused values that are cast to other things. // The problem was specified to casting to `*`, as creating unsafe // pointers was not being fully checked. Issue #20791. +//@ run-rustfix +#![allow(unused_variables)] fn main() { let x: &i32; diff --git a/tests/ui/borrowck/borrowck-use-uninitialized-in-cast.stderr b/tests/ui/borrowck/borrowck-use-uninitialized-in-cast.stderr index 7ccf6a4c3fc..22a3b721179 100644 --- a/tests/ui/borrowck/borrowck-use-uninitialized-in-cast.stderr +++ b/tests/ui/borrowck/borrowck-use-uninitialized-in-cast.stderr @@ -1,5 +1,5 @@ error[E0381]: used binding `x` isn't initialized - --> $DIR/borrowck-use-uninitialized-in-cast.rs:7:13 + --> $DIR/borrowck-use-uninitialized-in-cast.rs:9:13 | LL | let x: &i32; | - binding declared here but left uninitialized @@ -8,8 +8,8 @@ LL | let y = x as *const i32; | help: consider assigning a value | -LL | let x: &i32 = todo!(); - | +++++++++ +LL | let x: &i32 = &42; + | +++++ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-vec-pattern-nesting.rs b/tests/ui/borrowck/borrowck-vec-pattern-nesting.rs index 1bda7a49713..ec074d2cf1c 100644 --- a/tests/ui/borrowck/borrowck-vec-pattern-nesting.rs +++ b/tests/ui/borrowck/borrowck-vec-pattern-nesting.rs @@ -47,6 +47,7 @@ fn c() { //~| NOTE cannot move out of here //~| NOTE move occurs because //~| HELP consider borrowing here + //~| HELP consider cloning } fn d() { @@ -66,6 +67,7 @@ fn d() { //~| NOTE cannot move out of here //~| NOTE move occurs because //~| HELP consider borrowing here + //~| HELP consider cloning } fn e() { @@ -86,6 +88,7 @@ fn e() { //~| NOTE cannot move out of here //~| NOTE move occurs because //~| HELP consider borrowing here + //~| HELP consider cloning } fn main() {} diff --git a/tests/ui/borrowck/borrowck-vec-pattern-nesting.stderr b/tests/ui/borrowck/borrowck-vec-pattern-nesting.stderr index 024cb006c26..fff997fd555 100644 --- a/tests/ui/borrowck/borrowck-vec-pattern-nesting.stderr +++ b/tests/ui/borrowck/borrowck-vec-pattern-nesting.stderr @@ -53,9 +53,13 @@ help: consider borrowing here | LL | let a = &vec[0]; | + +help: consider cloning the value if the performance cost is acceptable + | +LL | let a = vec[0].clone(); + | ++++++++ error[E0508]: cannot move out of type `[Box<isize>]`, a non-copy slice - --> $DIR/borrowck-vec-pattern-nesting.rs:55:11 + --> $DIR/borrowck-vec-pattern-nesting.rs:56:11 | LL | match vec { | ^^^ cannot move out of here @@ -73,7 +77,7 @@ LL + [ | error[E0508]: cannot move out of type `[Box<isize>]`, a non-copy slice - --> $DIR/borrowck-vec-pattern-nesting.rs:65:13 + --> $DIR/borrowck-vec-pattern-nesting.rs:66:13 | LL | let a = vec[0]; | ^^^^^^ @@ -85,9 +89,13 @@ help: consider borrowing here | LL | let a = &vec[0]; | + +help: consider cloning the value if the performance cost is acceptable + | +LL | let a = vec[0].clone(); + | ++++++++ error[E0508]: cannot move out of type `[Box<isize>]`, a non-copy slice - --> $DIR/borrowck-vec-pattern-nesting.rs:74:11 + --> $DIR/borrowck-vec-pattern-nesting.rs:76:11 | LL | match vec { | ^^^ cannot move out of here @@ -106,7 +114,7 @@ LL + [_a, _b, _c] => {} | error[E0508]: cannot move out of type `[Box<isize>]`, a non-copy slice - --> $DIR/borrowck-vec-pattern-nesting.rs:85:13 + --> $DIR/borrowck-vec-pattern-nesting.rs:87:13 | LL | let a = vec[0]; | ^^^^^^ @@ -118,6 +126,10 @@ help: consider borrowing here | LL | let a = &vec[0]; | + +help: consider cloning the value if the performance cost is acceptable + | +LL | let a = vec[0].clone(); + | ++++++++ error: aborting due to 8 previous errors diff --git a/tests/ui/borrowck/clone-on-ref.stderr b/tests/ui/borrowck/clone-on-ref.stderr index ee4fcadf55a..f0eaf4bac7d 100644 --- a/tests/ui/borrowck/clone-on-ref.stderr +++ b/tests/ui/borrowck/clone-on-ref.stderr @@ -52,6 +52,11 @@ LL | LL | println!("{b:?}"); | ----- borrow later used here | +note: if `A` implemented `Clone`, you could clone the value + --> $DIR/clone-on-ref.rs:19:1 + | +LL | struct A; + | ^^^^^^^^ help: consider annotating `A` with `#[derive(Clone)]` | LL + #[derive(Clone)] diff --git a/tests/ui/borrowck/clone-span-on-try-operator.fixed b/tests/ui/borrowck/clone-span-on-try-operator.fixed index 59253c98079..59a162e72c1 100644 --- a/tests/ui/borrowck/clone-span-on-try-operator.fixed +++ b/tests/ui/borrowck/clone-span-on-try-operator.fixed @@ -7,5 +7,5 @@ impl Foo { } fn main() { let foo = &Foo; - <Foo as Clone>::clone(&(*foo)).foo(); //~ ERROR cannot move out + <Foo as Clone>::clone(&foo.clone()).foo(); //~ ERROR cannot move out } diff --git a/tests/ui/borrowck/clone-span-on-try-operator.stderr b/tests/ui/borrowck/clone-span-on-try-operator.stderr index adf84e49a9f..c2c63f94943 100644 --- a/tests/ui/borrowck/clone-span-on-try-operator.stderr +++ b/tests/ui/borrowck/clone-span-on-try-operator.stderr @@ -15,6 +15,11 @@ help: you can `clone` the value and consume it, but this might not be your desir | LL | <Foo as Clone>::clone(&(*foo)).foo(); | +++++++++++++++++++++++ + +help: consider cloning the value if the performance cost is acceptable + | +LL - (*foo).foo(); +LL + foo.clone().foo(); + | error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/issue-101119.stderr b/tests/ui/borrowck/issue-101119.stderr index 1f32ece3d3d..b4775496f4f 100644 --- a/tests/ui/borrowck/issue-101119.stderr +++ b/tests/ui/borrowck/issue-101119.stderr @@ -4,11 +4,25 @@ error[E0382]: use of moved value: `state` LL | fn fill_memory_blocks_mt(state: &mut State) { | ----- move occurs because `state` has type `&mut State`, which does not implement the `Copy` trait LL | loop { + | ---- inside of this loop LL | once(move || { | ^^^^^^^ value moved into closure here, in previous iteration of loop LL | LL | fill_segment(state); | ----- use occurs due to use in closure + | +note: consider changing this parameter type in function `fill_segment` to borrow instead if owning the value isn't necessary + --> $DIR/issue-101119.rs:14:20 + | +LL | fn fill_segment(_: &mut State) {} + | ------------ ^^^^^^^^^^ this parameter takes ownership of the value + | | + | in this function +note: if `State` implemented `Clone`, you could clone the value + --> $DIR/issue-101119.rs:1:1 + | +LL | struct State; + | ^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/issue-103250.stderr b/tests/ui/borrowck/issue-103250.stderr index b7ece5d971d..104bded5b0b 100644 --- a/tests/ui/borrowck/issue-103250.stderr +++ b/tests/ui/borrowck/issue-103250.stderr @@ -9,8 +9,8 @@ LL | Err(last_error) | help: consider assigning a value | -LL | let mut last_error: Box<dyn std::error::Error> = todo!(); - | +++++++++ +LL | let mut last_error: Box<dyn std::error::Error> = Box::new(value); + | +++++++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/issue-103624.stderr b/tests/ui/borrowck/issue-103624.stderr index 7a281e8aa30..94421c35c65 100644 --- a/tests/ui/borrowck/issue-103624.stderr +++ b/tests/ui/borrowck/issue-103624.stderr @@ -9,6 +9,12 @@ LL | spawn_blocking(move || { LL | LL | self.b; | ^^^^^^ move occurs because `self.b` has type `StructB`, which does not implement the `Copy` trait + | +note: if `StructB` implemented `Clone`, you could clone the value + --> $DIR/issue-103624.rs:23:1 + | +LL | struct StructB {} + | ^^^^^^^^^^^^^^ error[E0521]: borrowed data escapes outside of method --> $DIR/issue-103624.rs:14:9 diff --git a/tests/ui/borrowck/issue-119915-bad-clone-suggestion.stderr b/tests/ui/borrowck/issue-119915-bad-clone-suggestion.stderr index ab42205d510..701f00d079d 100644 --- a/tests/ui/borrowck/issue-119915-bad-clone-suggestion.stderr +++ b/tests/ui/borrowck/issue-119915-bad-clone-suggestion.stderr @@ -11,6 +11,11 @@ note: `Example::<E, FakeParam>::change` takes ownership of the receiver `self`, | LL | unsafe fn change<NewFakeParam>(self) -> Example<E, NewFakeParam> { | ^^^^ +note: if `Example<E, NoLifetime>` implemented `Clone`, you could clone the value + --> $DIR/issue-119915-bad-clone-suggestion.rs:3:1 + | +LL | struct Example<E, FakeParam>(PhantomData<(fn(E), fn(FakeParam))>); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/issue-17718-static-move.stderr b/tests/ui/borrowck/issue-17718-static-move.stderr index 5ca0a7fb885..e2c3a9d5a26 100644 --- a/tests/ui/borrowck/issue-17718-static-move.stderr +++ b/tests/ui/borrowck/issue-17718-static-move.stderr @@ -4,6 +4,11 @@ error[E0507]: cannot move out of static item `FOO` LL | let _a = FOO; | ^^^ move occurs because `FOO` has type `Foo`, which does not implement the `Copy` trait | +note: if `Foo` implemented `Clone`, you could clone the value + --> $DIR/issue-17718-static-move.rs:1:1 + | +LL | struct Foo; + | ^^^^^^^^^^ help: consider borrowing here | LL | let _a = &FOO; diff --git a/tests/ui/borrowck/issue-20801.stderr b/tests/ui/borrowck/issue-20801.stderr index 97294afd3df..1da6f0bef02 100644 --- a/tests/ui/borrowck/issue-20801.stderr +++ b/tests/ui/borrowck/issue-20801.stderr @@ -19,6 +19,11 @@ error[E0507]: cannot move out of a mutable reference LL | let a = unsafe { *mut_ref() }; | ^^^^^^^^^^ move occurs because value has type `T`, which does not implement the `Copy` trait | +note: if `T` implemented `Clone`, you could clone the value + --> $DIR/issue-20801.rs:3:1 + | +LL | struct T(u8); + | ^^^^^^^^ help: consider removing the dereference here | LL - let a = unsafe { *mut_ref() }; @@ -31,6 +36,11 @@ error[E0507]: cannot move out of a shared reference LL | let b = unsafe { *imm_ref() }; | ^^^^^^^^^^ move occurs because value has type `T`, which does not implement the `Copy` trait | +note: if `T` implemented `Clone`, you could clone the value + --> $DIR/issue-20801.rs:3:1 + | +LL | struct T(u8); + | ^^^^^^^^ help: consider removing the dereference here | LL - let b = unsafe { *imm_ref() }; @@ -43,6 +53,11 @@ error[E0507]: cannot move out of a raw pointer LL | let c = unsafe { *mut_ptr() }; | ^^^^^^^^^^ move occurs because value has type `T`, which does not implement the `Copy` trait | +note: if `T` implemented `Clone`, you could clone the value + --> $DIR/issue-20801.rs:3:1 + | +LL | struct T(u8); + | ^^^^^^^^ help: consider removing the dereference here | LL - let c = unsafe { *mut_ptr() }; @@ -55,6 +70,11 @@ error[E0507]: cannot move out of a raw pointer LL | let d = unsafe { *const_ptr() }; | ^^^^^^^^^^^^ move occurs because value has type `T`, which does not implement the `Copy` trait | +note: if `T` implemented `Clone`, you could clone the value + --> $DIR/issue-20801.rs:3:1 + | +LL | struct T(u8); + | ^^^^^^^^ help: consider removing the dereference here | LL - let d = unsafe { *const_ptr() }; diff --git a/tests/ui/borrowck/issue-24267-flow-exit.stderr b/tests/ui/borrowck/issue-24267-flow-exit.stderr index 58d1c8c0f73..216f8d49b1b 100644 --- a/tests/ui/borrowck/issue-24267-flow-exit.stderr +++ b/tests/ui/borrowck/issue-24267-flow-exit.stderr @@ -10,8 +10,8 @@ LL | println!("{}", x); = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider assigning a value | -LL | let x: i32 = 0; - | +++ +LL | let x: i32 = 42; + | ++++ error[E0381]: used binding `x` isn't initialized --> $DIR/issue-24267-flow-exit.rs:18:20 @@ -25,8 +25,8 @@ LL | println!("{}", x); = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider assigning a value | -LL | let x: i32 = 0; - | +++ +LL | let x: i32 = 42; + | ++++ error: aborting due to 2 previous errors diff --git a/tests/ui/borrowck/issue-62107-match-arm-scopes.stderr b/tests/ui/borrowck/issue-62107-match-arm-scopes.stderr index e19f37538c1..8705b8450fc 100644 --- a/tests/ui/borrowck/issue-62107-match-arm-scopes.stderr +++ b/tests/ui/borrowck/issue-62107-match-arm-scopes.stderr @@ -9,8 +9,8 @@ LL | ref u if true => {} | help: consider assigning a value | -LL | let e: i32 = 0; - | +++ +LL | let e: i32 = 42; + | ++++ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/issue-64453.stderr b/tests/ui/borrowck/issue-64453.stderr index f032ea779dd..0e4a8d42f6e 100644 --- a/tests/ui/borrowck/issue-64453.stderr +++ b/tests/ui/borrowck/issue-64453.stderr @@ -22,6 +22,11 @@ error[E0507]: cannot move out of static item `settings_dir` | LL | let settings_data = from_string(settings_dir); | ^^^^^^^^^^^^ move occurs because `settings_dir` has type `String`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let settings_data = from_string(settings_dir.clone()); + | ++++++++ error: aborting due to 3 previous errors diff --git a/tests/ui/borrowck/issue-87456-point-to-closure.stderr b/tests/ui/borrowck/issue-87456-point-to-closure.stderr index a15909df07b..a0c7cac2add 100644 --- a/tests/ui/borrowck/issue-87456-point-to-closure.stderr +++ b/tests/ui/borrowck/issue-87456-point-to-closure.stderr @@ -14,6 +14,10 @@ help: consider borrowing here | LL | let _foo: String = &val; | + +help: consider cloning the value if the performance cost is acceptable + | +LL | let _foo: String = val.clone(); + | ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/move-error-in-promoted-2.stderr b/tests/ui/borrowck/move-error-in-promoted-2.stderr index 0d5edadcb46..43f4e820857 100644 --- a/tests/ui/borrowck/move-error-in-promoted-2.stderr +++ b/tests/ui/borrowck/move-error-in-promoted-2.stderr @@ -6,6 +6,12 @@ LL | &([S][0],); | | | cannot move out of here | move occurs because value has type `S`, which does not implement the `Copy` trait + | +note: if `S` implemented `Clone`, you could clone the value + --> $DIR/move-error-in-promoted-2.rs:3:1 + | +LL | struct S; + | ^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/move-error-in-promoted.stderr b/tests/ui/borrowck/move-error-in-promoted.stderr index 03c0297c5a9..8d42df24e27 100644 --- a/tests/ui/borrowck/move-error-in-promoted.stderr +++ b/tests/ui/borrowck/move-error-in-promoted.stderr @@ -6,6 +6,11 @@ LL | let _ = S1(C[0]).clone(); | | | cannot move out of here | move occurs because value has type `S2`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let _ = S1(C[0].clone()).clone(); + | ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/move-error-snippets.stderr b/tests/ui/borrowck/move-error-snippets.stderr index 83f9e19aa0d..40b64398aef 100644 --- a/tests/ui/borrowck/move-error-snippets.stderr +++ b/tests/ui/borrowck/move-error-snippets.stderr @@ -9,6 +9,11 @@ LL | let a = $c; LL | sss!(); | ------ in this macro invocation | +note: if `A` implemented `Clone`, you could clone the value + --> $DIR/move-error-snippets.rs:9:1 + | +LL | struct A; + | ^^^^^^^^ = note: this error originates in the macro `aaa` which comes from the expansion of the macro `sss` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider borrowing here | diff --git a/tests/ui/borrowck/move-from-union-field-issue-66500.stderr b/tests/ui/borrowck/move-from-union-field-issue-66500.stderr index 70078582713..c951ce8e3cd 100644 --- a/tests/ui/borrowck/move-from-union-field-issue-66500.stderr +++ b/tests/ui/borrowck/move-from-union-field-issue-66500.stderr @@ -3,24 +3,48 @@ error[E0507]: cannot move out of `*u.a` which is behind a shared reference | LL | *u.a | ^^^^ move occurs because `*u.a` has type `String`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL - *u.a +LL + u.a.clone() + | error[E0507]: cannot move out of `*u.b` which is behind a mutable reference --> $DIR/move-from-union-field-issue-66500.rs:16:5 | LL | *u.b | ^^^^ move occurs because `*u.b` has type `String`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL - *u.b +LL + u.b.clone() + | error[E0507]: cannot move out of `*u.c` which is behind a raw pointer --> $DIR/move-from-union-field-issue-66500.rs:20:5 | LL | *u.c | ^^^^ move occurs because `*u.c` has type `String`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL - *u.c +LL + u.c.clone() + | error[E0507]: cannot move out of `*u.d` which is behind a raw pointer --> $DIR/move-from-union-field-issue-66500.rs:24:5 | LL | *u.d | ^^^^ move occurs because `*u.d` has type `String`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL - *u.d +LL + u.d.clone() + | error: aborting due to 4 previous errors diff --git a/tests/ui/borrowck/move-in-static-initializer-issue-38520.stderr b/tests/ui/borrowck/move-in-static-initializer-issue-38520.stderr index 6619fb42c28..a4e70b50646 100644 --- a/tests/ui/borrowck/move-in-static-initializer-issue-38520.stderr +++ b/tests/ui/borrowck/move-in-static-initializer-issue-38520.stderr @@ -3,12 +3,24 @@ error[E0507]: cannot move out of a shared reference | LL | static Y: usize = get(*&X); | ^^^ move occurs because value has type `Foo`, which does not implement the `Copy` trait + | +note: if `Foo` implemented `Clone`, you could clone the value + --> $DIR/move-in-static-initializer-issue-38520.rs:5:1 + | +LL | struct Foo(usize); + | ^^^^^^^^^^ error[E0507]: cannot move out of a shared reference --> $DIR/move-in-static-initializer-issue-38520.rs:13:22 | LL | const Z: usize = get(*&X); | ^^^ move occurs because value has type `Foo`, which does not implement the `Copy` trait + | +note: if `Foo` implemented `Clone`, you could clone the value + --> $DIR/move-in-static-initializer-issue-38520.rs:5:1 + | +LL | struct Foo(usize); + | ^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/tests/ui/borrowck/suggest-assign-rvalue.stderr b/tests/ui/borrowck/suggest-assign-rvalue.stderr index 92acba640d7..4305539f1b6 100644 --- a/tests/ui/borrowck/suggest-assign-rvalue.stderr +++ b/tests/ui/borrowck/suggest-assign-rvalue.stderr @@ -8,8 +8,8 @@ LL | apple(chaenomeles); | help: consider assigning a value | -LL | let chaenomeles = 0; - | +++ +LL | let chaenomeles = 42; + | ++++ error[E0381]: used binding `my_float` isn't initialized --> $DIR/suggest-assign-rvalue.rs:23:30 @@ -22,8 +22,8 @@ LL | println!("my_float: {}", my_float); = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider assigning a value | -LL | let my_float: f32 = 0.0; - | +++++ +LL | let my_float: f32 = 3.14159; + | +++++++++ error[E0381]: used binding `demo` isn't initialized --> $DIR/suggest-assign-rvalue.rs:26:28 @@ -50,8 +50,8 @@ LL | println!("demo_no: {:?}", demo_no); = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider assigning a value | -LL | let demo_no: DemoNoDef = todo!(); - | +++++++++ +LL | let demo_no: DemoNoDef = value; + | +++++++ error[E0381]: used binding `arr` isn't initialized --> $DIR/suggest-assign-rvalue.rs:34:27 @@ -64,7 +64,7 @@ LL | println!("arr: {:?}", arr); = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider assigning a value | -LL | let arr: [i32; 5] = todo!(); +LL | let arr: [i32; 5] = [42; 5]; | +++++++++ error[E0381]: used binding `foo` isn't initialized @@ -106,8 +106,8 @@ LL | println!("my_int: {}", *my_int); = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider assigning a value | -LL | let my_int: &i32 = todo!(); - | +++++++++ +LL | let my_int: &i32 = &42; + | +++++ error[E0381]: used binding `hello` isn't initialized --> $DIR/suggest-assign-rvalue.rs:49:27 @@ -120,8 +120,8 @@ LL | println!("hello: {}", hello); = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider assigning a value | -LL | let hello: &str = todo!(); - | +++++++++ +LL | let hello: &str = ""; + | ++++ error[E0381]: used binding `never` isn't initialized --> $DIR/suggest-assign-rvalue.rs:53:27 diff --git a/tests/ui/borrowck/trait-impl-argument-difference-ice.rs b/tests/ui/borrowck/trait-impl-argument-difference-ice.rs new file mode 100644 index 00000000000..872507cc4de --- /dev/null +++ b/tests/ui/borrowck/trait-impl-argument-difference-ice.rs @@ -0,0 +1,25 @@ +// Issue https://github.com/rust-lang/rust/issues/123414 +trait MemoryUnit { + extern "C" fn read_word(&mut self) -> u8; + extern "C" fn read_dword(Self::Assoc<'_>) -> u16; + //~^ WARNING anonymous parameters are deprecated and will be removed in the next edition + //~| WARNING this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018! + //~| ERROR associated type `Assoc` not found for `Self` +} + +struct ROM {} + +impl MemoryUnit for ROM { +//~^ ERROR not all trait items implemented, missing: `read_word` + extern "C" fn read_dword(&'_ self) -> u16 { + //~^ ERROR method `read_dword` has a `&self` declaration in the impl, but not in the trait + let a16 = self.read_word() as u16; + //~^ ERROR cannot borrow `*self` as mutable, as it is behind a `&` reference + let b16 = self.read_word() as u16; + //~^ ERROR cannot borrow `*self` as mutable, as it is behind a `&` reference + + (b16 << 8) | a16 + } +} + +pub fn main() {} diff --git a/tests/ui/borrowck/trait-impl-argument-difference-ice.stderr b/tests/ui/borrowck/trait-impl-argument-difference-ice.stderr new file mode 100644 index 00000000000..5c70eccfbd3 --- /dev/null +++ b/tests/ui/borrowck/trait-impl-argument-difference-ice.stderr @@ -0,0 +1,60 @@ +warning: anonymous parameters are deprecated and will be removed in the next edition + --> $DIR/trait-impl-argument-difference-ice.rs:4:30 + | +LL | extern "C" fn read_dword(Self::Assoc<'_>) -> u16; + | ^^^^^^^^^^^^^^^ help: try naming the parameter or explicitly ignoring it: `_: Self::Assoc<'_>` + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018! + = note: for more information, see issue #41686 <https://github.com/rust-lang/rust/issues/41686> + = note: `#[warn(anonymous_parameters)]` on by default + +error[E0220]: associated type `Assoc` not found for `Self` + --> $DIR/trait-impl-argument-difference-ice.rs:4:36 + | +LL | extern "C" fn read_dword(Self::Assoc<'_>) -> u16; + | ^^^^^ associated type `Assoc` not found + +error[E0185]: method `read_dword` has a `&self` declaration in the impl, but not in the trait + --> $DIR/trait-impl-argument-difference-ice.rs:14:5 + | +LL | extern "C" fn read_dword(Self::Assoc<'_>) -> u16; + | ------------------------------------------------- trait method declared without `&self` +... +LL | extern "C" fn read_dword(&'_ self) -> u16 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `&self` used in impl + +error[E0046]: not all trait items implemented, missing: `read_word` + --> $DIR/trait-impl-argument-difference-ice.rs:12:1 + | +LL | extern "C" fn read_word(&mut self) -> u8; + | ----------------------------------------- `read_word` from trait +... +LL | impl MemoryUnit for ROM { + | ^^^^^^^^^^^^^^^^^^^^^^^ missing `read_word` in implementation + +error[E0596]: cannot borrow `*self` as mutable, as it is behind a `&` reference + --> $DIR/trait-impl-argument-difference-ice.rs:16:19 + | +LL | let a16 = self.read_word() as u16; + | ^^^^ `self` is a `&` reference, so the data it refers to cannot be borrowed as mutable + | +help: consider changing this to be a mutable reference in the `impl` method and the `trait` definition + | +LL | extern "C" fn read_dword(&'_ mut self) -> u16 { + | ~~~~~~~~~~~~ + +error[E0596]: cannot borrow `*self` as mutable, as it is behind a `&` reference + --> $DIR/trait-impl-argument-difference-ice.rs:18:19 + | +LL | let b16 = self.read_word() as u16; + | ^^^^ `self` is a `&` reference, so the data it refers to cannot be borrowed as mutable + | +help: consider changing this to be a mutable reference in the `impl` method and the `trait` definition + | +LL | extern "C" fn read_dword(&'_ mut self) -> u16 { + | ~~~~~~~~~~~~ + +error: aborting due to 5 previous errors; 1 warning emitted + +Some errors have detailed explanations: E0046, E0185, E0220, E0596. +For more information about an error, try `rustc --explain E0046`. diff --git a/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.fixed b/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.fixed index 8add3a5f2b6..3b4f7c8465c 100644 --- a/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.fixed +++ b/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.fixed @@ -9,7 +9,7 @@ fn call<F>(f: F) where F : Fn() { fn main() { let y = vec![format!("World")]; call(|| { - <Vec<String> as Clone>::clone(&y).into_iter(); + <Vec<String> as Clone>::clone(&y.clone()).into_iter(); //~^ ERROR cannot move out of `y`, a captured variable in an `Fn` closure }); } diff --git a/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr b/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr index a2ff70255f5..177e9c8d248 100644 --- a/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr +++ b/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr @@ -16,6 +16,10 @@ help: you can `clone` the value and consume it, but this might not be your desir | LL | <Vec<String> as Clone>::clone(&y).into_iter(); | +++++++++++++++++++++++++++++++ + +help: consider cloning the value if the performance cost is acceptable + | +LL | y.clone().into_iter(); + | ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/box/leak-alloc.stderr b/tests/ui/box/leak-alloc.stderr index 8b8cea3fe84..53ff5f0107d 100644 --- a/tests/ui/box/leak-alloc.stderr +++ b/tests/ui/box/leak-alloc.stderr @@ -11,6 +11,12 @@ LL | drop(alloc); LL | LL | use_value(*theref) | ------- borrow later used here + | +note: if `Alloc` implemented `Clone`, you could clone the value + --> $DIR/leak-alloc.rs:8:1 + | +LL | struct Alloc {} + | ^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/btreemap/btreemap_dropck.stderr b/tests/ui/btreemap/btreemap_dropck.stderr index 805c2112bdc..873f8cf9a01 100644 --- a/tests/ui/btreemap/btreemap_dropck.stderr +++ b/tests/ui/btreemap/btreemap_dropck.stderr @@ -9,6 +9,12 @@ LL | drop(s); | ^ move out of `s` occurs here LL | } | - borrow might be used here, when `_map` is dropped and runs the `Drop` code for type `BTreeMap` + | +help: consider cloning the value if the performance cost is acceptable + | +LL - let _map = BTreeMap::from_iter([((), PrintOnDrop(&s))]); +LL + let _map = BTreeMap::from_iter([((), PrintOnDrop(s.clone()))]); + | error: aborting due to 1 previous error diff --git a/tests/ui/check-static-values-constraints.stderr b/tests/ui/check-static-values-constraints.stderr index dee1f2b1210..fe5f2a34272 100644 --- a/tests/ui/check-static-values-constraints.stderr +++ b/tests/ui/check-static-values-constraints.stderr @@ -160,6 +160,10 @@ help: consider borrowing here | LL | &x | + +help: consider cloning the value if the performance cost is acceptable + | +LL | x.clone() + | ++++++++ error: aborting due to 17 previous errors diff --git a/tests/ui/closures/2229_closure_analysis/match/pattern-matching-should-fail.stderr b/tests/ui/closures/2229_closure_analysis/match/pattern-matching-should-fail.stderr index 8a32f0d99e7..f8ed792e3c6 100644 --- a/tests/ui/closures/2229_closure_analysis/match/pattern-matching-should-fail.stderr +++ b/tests/ui/closures/2229_closure_analysis/match/pattern-matching-should-fail.stderr @@ -79,8 +79,8 @@ LL | let c1 = || match x { }; | help: consider assigning a value | -LL | let x: u8 = 0; - | +++ +LL | let x: u8 = 42; + | ++++ error: aborting due to 8 previous errors diff --git a/tests/ui/closures/return-value-lifetime-error.fixed b/tests/ui/closures/return-value-lifetime-error.fixed new file mode 100644 index 00000000000..bf1f7e4a6cf --- /dev/null +++ b/tests/ui/closures/return-value-lifetime-error.fixed @@ -0,0 +1,16 @@ +//@ run-rustfix +use std::collections::HashMap; + +fn main() { + let vs = vec![0, 0, 1, 1, 3, 4, 5, 6, 3, 3, 3]; + + let mut counts = HashMap::new(); + for num in vs { + let count = counts.entry(num).or_insert(0); + *count += 1; + } + + let _ = counts.iter().max_by_key(|(_, v)| **v); + //~^ ERROR lifetime may not live long enough + //~| HELP dereference the return value +} diff --git a/tests/ui/closures/return-value-lifetime-error.rs b/tests/ui/closures/return-value-lifetime-error.rs new file mode 100644 index 00000000000..411c91f413e --- /dev/null +++ b/tests/ui/closures/return-value-lifetime-error.rs @@ -0,0 +1,16 @@ +//@ run-rustfix +use std::collections::HashMap; + +fn main() { + let vs = vec![0, 0, 1, 1, 3, 4, 5, 6, 3, 3, 3]; + + let mut counts = HashMap::new(); + for num in vs { + let count = counts.entry(num).or_insert(0); + *count += 1; + } + + let _ = counts.iter().max_by_key(|(_, v)| v); + //~^ ERROR lifetime may not live long enough + //~| HELP dereference the return value +} diff --git a/tests/ui/closures/return-value-lifetime-error.stderr b/tests/ui/closures/return-value-lifetime-error.stderr new file mode 100644 index 00000000000..a0ad127db28 --- /dev/null +++ b/tests/ui/closures/return-value-lifetime-error.stderr @@ -0,0 +1,16 @@ +error: lifetime may not live long enough + --> $DIR/return-value-lifetime-error.rs:13:47 + | +LL | let _ = counts.iter().max_by_key(|(_, v)| v); + | ------- ^ returning this value requires that `'1` must outlive `'2` + | | | + | | return type of closure is &'2 &i32 + | has type `&'1 (&i32, &i32)` + | +help: dereference the return value + | +LL | let _ = counts.iter().max_by_key(|(_, v)| **v); + | ++ + +error: aborting due to 1 previous error + diff --git a/tests/ui/const-generics/const-generic-default-wont-borrowck.fixed b/tests/ui/const-generics/const-generic-default-wont-borrowck.fixed new file mode 100644 index 00000000000..da48c62df21 --- /dev/null +++ b/tests/ui/const-generics/const-generic-default-wont-borrowck.fixed @@ -0,0 +1,6 @@ +//@ run-rustfix +pub struct X<const N: usize = { + let s: &'static str = ""; s.len() //~ ERROR E0381 +}>; + +fn main() {} diff --git a/tests/ui/const-generics/const-generic-default-wont-borrowck.rs b/tests/ui/const-generics/const-generic-default-wont-borrowck.rs index e64adacac9f..0d7d87100b7 100644 --- a/tests/ui/const-generics/const-generic-default-wont-borrowck.rs +++ b/tests/ui/const-generics/const-generic-default-wont-borrowck.rs @@ -1,4 +1,5 @@ -struct X<const N: usize = { +//@ run-rustfix +pub struct X<const N: usize = { let s: &'static str; s.len() //~ ERROR E0381 }>; diff --git a/tests/ui/const-generics/const-generic-default-wont-borrowck.stderr b/tests/ui/const-generics/const-generic-default-wont-borrowck.stderr index 4cea35f1c8e..83e7f88eda7 100644 --- a/tests/ui/const-generics/const-generic-default-wont-borrowck.stderr +++ b/tests/ui/const-generics/const-generic-default-wont-borrowck.stderr @@ -1,5 +1,5 @@ error[E0381]: used binding `s` isn't initialized - --> $DIR/const-generic-default-wont-borrowck.rs:2:26 + --> $DIR/const-generic-default-wont-borrowck.rs:3:26 | LL | let s: &'static str; s.len() | - ^ `*s` used here but it isn't initialized @@ -8,8 +8,8 @@ LL | let s: &'static str; s.len() | help: consider assigning a value | -LL | let s: &'static str = todo!(); s.len() - | +++++++++ +LL | let s: &'static str = ""; s.len() + | ++++ error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/defaults/doesnt_infer.rs b/tests/ui/const-generics/defaults/doesnt_infer.rs index 9c59e672d8e..e14c08fc148 100644 --- a/tests/ui/const-generics/defaults/doesnt_infer.rs +++ b/tests/ui/const-generics/defaults/doesnt_infer.rs @@ -9,5 +9,5 @@ impl<const N: u32> Foo<N> { fn main() { let foo = Foo::<1>::foo(); let foo = Foo::foo(); - //~^ error: type annotations needed for `Foo<N>` + //~^ ERROR type annotations needed for `Foo<_>` } diff --git a/tests/ui/const-generics/defaults/doesnt_infer.stderr b/tests/ui/const-generics/defaults/doesnt_infer.stderr index 65ee0ecfdc5..93d58603397 100644 --- a/tests/ui/const-generics/defaults/doesnt_infer.stderr +++ b/tests/ui/const-generics/defaults/doesnt_infer.stderr @@ -1,4 +1,4 @@ -error[E0282]: type annotations needed for `Foo<N>` +error[E0282]: type annotations needed for `Foo<_>` --> $DIR/doesnt_infer.rs:11:9 | LL | let foo = Foo::foo(); diff --git a/tests/ui/const-generics/generic_arg_infer/issue-91614.stderr b/tests/ui/const-generics/generic_arg_infer/issue-91614.stderr index 5b296a14869..5ee42c19dd3 100644 --- a/tests/ui/const-generics/generic_arg_infer/issue-91614.stderr +++ b/tests/ui/const-generics/generic_arg_infer/issue-91614.stderr @@ -1,4 +1,4 @@ -error[E0283]: type annotations needed for `Mask<_, N>` +error[E0283]: type annotations needed for `Mask<_, _>` --> $DIR/issue-91614.rs:6:9 | LL | let y = Mask::<_, _>::splat(false); diff --git a/tests/ui/const-generics/generic_const_exprs/issue-62504.full.stderr b/tests/ui/const-generics/generic_const_exprs/issue-62504.full.stderr index f27d52a1437..5cda4681b5c 100644 --- a/tests/ui/const-generics/generic_const_exprs/issue-62504.full.stderr +++ b/tests/ui/const-generics/generic_const_exprs/issue-62504.full.stderr @@ -18,7 +18,7 @@ help: try adding a `where` bound LL | pub const fn new() -> Self where [(); Self::SIZE]: { | +++++++++++++++++++++++ -error[E0282]: type annotations needed for `ArrayHolder<X>` +error[E0282]: type annotations needed for `ArrayHolder<_>` --> $DIR/issue-62504.rs:26:9 | LL | let mut array = ArrayHolder::new(); diff --git a/tests/ui/const-generics/generic_const_exprs/issue-62504.min.stderr b/tests/ui/const-generics/generic_const_exprs/issue-62504.min.stderr index 1664669eee0..beb159779ff 100644 --- a/tests/ui/const-generics/generic_const_exprs/issue-62504.min.stderr +++ b/tests/ui/const-generics/generic_const_exprs/issue-62504.min.stderr @@ -22,7 +22,7 @@ note: tuple struct defined here LL | struct ArrayHolder<const X: usize>([u32; X]); | ^^^^^^^^^^^ -error[E0282]: type annotations needed for `ArrayHolder<X>` +error[E0282]: type annotations needed for `ArrayHolder<_>` --> $DIR/issue-62504.rs:26:9 | LL | let mut array = ArrayHolder::new(); diff --git a/tests/ui/consts/const_fn_unsize.rs b/tests/ui/consts/const_fn_unsize.rs index f96a6088fd3..15d717e7e12 100644 --- a/tests/ui/consts/const_fn_unsize.rs +++ b/tests/ui/consts/const_fn_unsize.rs @@ -1,5 +1,4 @@ //@ run-pass -#![feature(slice_ptr_len)] use std::ptr::NonNull; diff --git a/tests/ui/consts/issue-78655.stderr b/tests/ui/consts/issue-78655.stderr index 5a38d023d6f..ccaed03b4c1 100644 --- a/tests/ui/consts/issue-78655.stderr +++ b/tests/ui/consts/issue-78655.stderr @@ -8,8 +8,8 @@ LL | &x | help: consider assigning a value | -LL | let x = 0; - | +++ +LL | let x = 42; + | ++++ error: could not evaluate constant pattern --> $DIR/issue-78655.rs:7:9 diff --git a/tests/ui/derives/deriving-with-repr-packed-move-errors.stderr b/tests/ui/derives/deriving-with-repr-packed-move-errors.stderr index c538061b365..2de4ee4eabd 100644 --- a/tests/ui/derives/deriving-with-repr-packed-move-errors.stderr +++ b/tests/ui/derives/deriving-with-repr-packed-move-errors.stderr @@ -8,6 +8,10 @@ LL | struct StructA(String); | = note: `#[derive(Debug)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider cloning the value if the performance cost is acceptable + | +LL | struct StructA(String.clone()); + | ++++++++ error[E0507]: cannot move out of `self` which is behind a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:13:16 @@ -19,6 +23,10 @@ LL | struct StructA(String); | = note: `#[derive(PartialEq)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider cloning the value if the performance cost is acceptable + | +LL | struct StructA(String.clone()); + | ++++++++ error[E0507]: cannot move out of `other` which is behind a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:13:16 @@ -30,6 +38,10 @@ LL | struct StructA(String); | = note: `#[derive(PartialEq)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider cloning the value if the performance cost is acceptable + | +LL | struct StructA(String.clone()); + | ++++++++ error[E0507]: cannot move out of `self` which is behind a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:13:16 @@ -41,6 +53,10 @@ LL | struct StructA(String); | = note: `#[derive(PartialOrd)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour = note: this error originates in the derive macro `PartialOrd` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider cloning the value if the performance cost is acceptable + | +LL | struct StructA(String.clone()); + | ++++++++ error[E0507]: cannot move out of `other` which is behind a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:13:16 @@ -52,6 +68,10 @@ LL | struct StructA(String); | = note: `#[derive(PartialOrd)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour = note: this error originates in the derive macro `PartialOrd` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider cloning the value if the performance cost is acceptable + | +LL | struct StructA(String.clone()); + | ++++++++ error[E0507]: cannot move out of `self` which is behind a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:13:16 @@ -63,6 +83,10 @@ LL | struct StructA(String); | = note: `#[derive(Ord)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour = note: this error originates in the derive macro `Ord` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider cloning the value if the performance cost is acceptable + | +LL | struct StructA(String.clone()); + | ++++++++ error[E0507]: cannot move out of `other` which is behind a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:13:16 @@ -74,6 +98,10 @@ LL | struct StructA(String); | = note: `#[derive(Ord)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour = note: this error originates in the derive macro `Ord` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider cloning the value if the performance cost is acceptable + | +LL | struct StructA(String.clone()); + | ++++++++ error[E0507]: cannot move out of `self` which is behind a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:13:16 @@ -85,6 +113,10 @@ LL | struct StructA(String); | = note: `#[derive(Hash)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour = note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider cloning the value if the performance cost is acceptable + | +LL | struct StructA(String.clone()); + | ++++++++ error[E0507]: cannot move out of `self` which is behind a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:13:16 @@ -96,78 +128,142 @@ LL | struct StructA(String); | = note: `#[derive(Clone)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider cloning the value if the performance cost is acceptable + | +LL | struct StructA(String.clone()); + | ++++++++ error[E0507]: cannot move out of `self` which is behind a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:28:9 | LL | self.0 | ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL | self.0.clone() + | ++++++++ error[E0507]: cannot move out of `self` which is behind a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:38:20 | LL | let x = &{ self.0 }; | ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let x = &{ self.0.clone() }; + | ++++++++ error[E0507]: cannot move out of `self` which is behind a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:45:12 | LL | ({ self.0 }) == ({ other.0 }) | ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL | ({ self.0.clone() }) == ({ other.0 }) + | ++++++++ error[E0507]: cannot move out of `other` which is behind a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:45:28 | LL | ({ self.0 }) == ({ other.0 }) | ^^^^^^^ move occurs because `other.0` has type `String`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL | ({ self.0 }) == ({ other.0.clone() }) + | ++++++++ error[E0507]: cannot move out of `self` which is behind a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:53:36 | LL | PartialOrd::partial_cmp(&{ self.0 }, &{ other.0 }) | ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL | PartialOrd::partial_cmp(&{ self.0.clone() }, &{ other.0 }) + | ++++++++ error[E0507]: cannot move out of `other` which is behind a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:53:49 | LL | PartialOrd::partial_cmp(&{ self.0 }, &{ other.0 }) | ^^^^^^^ move occurs because `other.0` has type `String`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL | PartialOrd::partial_cmp(&{ self.0 }, &{ other.0.clone() }) + | ++++++++ error[E0507]: cannot move out of `self` which is behind a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:68:20 | LL | let x = &{ self.0 }; | ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let x = &{ self.0.clone() }; + | ++++++++ error[E0507]: cannot move out of `self` which is behind a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:75:12 | LL | ({ self.0 }) == ({ other.0 }) | ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL | ({ self.0.clone() }) == ({ other.0 }) + | ++++++++ error[E0507]: cannot move out of `other` which is behind a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:75:28 | LL | ({ self.0 }) == ({ other.0 }) | ^^^^^^^ move occurs because `other.0` has type `String`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL | ({ self.0 }) == ({ other.0.clone() }) + | ++++++++ error[E0507]: cannot move out of `self` which is behind a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:83:36 | LL | PartialOrd::partial_cmp(&{ self.0 }, &{ other.0 }) | ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL | PartialOrd::partial_cmp(&{ self.0.clone() }, &{ other.0 }) + | ++++++++ error[E0507]: cannot move out of `other` which is behind a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:83:49 | LL | PartialOrd::partial_cmp(&{ self.0 }, &{ other.0 }) | ^^^^^^^ move occurs because `other.0` has type `String`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL | PartialOrd::partial_cmp(&{ self.0 }, &{ other.0.clone() }) + | ++++++++ error[E0507]: cannot move out of `arg` which is behind a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:92:5 | LL | arg.0 | ^^^^^ move occurs because `arg.0` has type `String`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL | arg.0.clone() + | ++++++++ error: aborting due to 21 previous errors diff --git a/tests/ui/derives/deriving-with-repr-packed.stderr b/tests/ui/derives/deriving-with-repr-packed.stderr index 151be6901b0..26ac532263f 100644 --- a/tests/ui/derives/deriving-with-repr-packed.stderr +++ b/tests/ui/derives/deriving-with-repr-packed.stderr @@ -36,6 +36,11 @@ LL | #[repr(packed)] LL | struct X(Y); | ^ move occurs because `self.0` has type `Y`, which does not implement the `Copy` trait | +note: if `Y` implemented `Clone`, you could clone the value + --> $DIR/deriving-with-repr-packed.rs:16:1 + | +LL | struct Y(usize); + | ^^^^^^^^ = note: `#[derive(Debug)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/drop/repeat-drop-2.stderr b/tests/ui/drop/repeat-drop-2.stderr index 009a2057212..cea7baf6976 100644 --- a/tests/ui/drop/repeat-drop-2.stderr +++ b/tests/ui/drop/repeat-drop-2.stderr @@ -32,8 +32,8 @@ LL | let _ = [x; 0]; | help: consider assigning a value | -LL | let x: u8 = 0; - | +++ +LL | let x: u8 = 42; + | ++++ error: aborting due to 3 previous errors diff --git a/tests/ui/dropck/drop-with-active-borrows-1.stderr b/tests/ui/dropck/drop-with-active-borrows-1.stderr index 229514c6fee..7d1633267f0 100644 --- a/tests/ui/dropck/drop-with-active-borrows-1.stderr +++ b/tests/ui/dropck/drop-with-active-borrows-1.stderr @@ -9,6 +9,11 @@ LL | drop(a); | ^ move out of `a` occurs here LL | for s in &b { | -- borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let b: Vec<&str> = a.clone().lines().collect(); + | ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/error-codes/E0449.fixed b/tests/ui/error-codes/E0449.fixed new file mode 100644 index 00000000000..c7b4566303d --- /dev/null +++ b/tests/ui/error-codes/E0449.fixed @@ -0,0 +1,18 @@ +//@ run-rustfix + +#![allow(warnings)] + +struct Bar; + +trait Foo { + fn foo(); +} + + impl Bar {} //~ ERROR E0449 + + impl Foo for Bar { //~ ERROR E0449 + fn foo() {} //~ ERROR E0449 +} + +fn main() { +} diff --git a/tests/ui/error-codes/E0449.rs b/tests/ui/error-codes/E0449.rs index eba0d479e97..32d9b35169c 100644 --- a/tests/ui/error-codes/E0449.rs +++ b/tests/ui/error-codes/E0449.rs @@ -1,3 +1,7 @@ +//@ run-rustfix + +#![allow(warnings)] + struct Bar; trait Foo { diff --git a/tests/ui/error-codes/E0449.stderr b/tests/ui/error-codes/E0449.stderr index cf41bcce8c2..c6a98269a19 100644 --- a/tests/ui/error-codes/E0449.stderr +++ b/tests/ui/error-codes/E0449.stderr @@ -1,24 +1,24 @@ error[E0449]: visibility qualifiers are not permitted here - --> $DIR/E0449.rs:7:1 + --> $DIR/E0449.rs:11:1 | LL | pub impl Bar {} - | ^^^ + | ^^^ help: remove the qualifier | = note: place qualifiers on individual impl items instead error[E0449]: visibility qualifiers are not permitted here - --> $DIR/E0449.rs:9:1 + --> $DIR/E0449.rs:13:1 | LL | pub impl Foo for Bar { - | ^^^ + | ^^^ help: remove the qualifier | = note: trait items always share the visibility of their trait error[E0449]: visibility qualifiers are not permitted here - --> $DIR/E0449.rs:10:5 + --> $DIR/E0449.rs:14:5 | LL | pub fn foo() {} - | ^^^ + | ^^^ help: remove the qualifier | = note: trait items always share the visibility of their trait diff --git a/tests/ui/error-codes/E0504.stderr b/tests/ui/error-codes/E0504.stderr index c8a48961cb3..900cb706bd9 100644 --- a/tests/ui/error-codes/E0504.stderr +++ b/tests/ui/error-codes/E0504.stderr @@ -13,6 +13,12 @@ LL | println!("child function: {}", fancy_num.num); ... LL | println!("main function: {}", fancy_ref.num); | ------------- borrow later used here + | +note: if `FancyNum` implemented `Clone`, you could clone the value + --> $DIR/E0504.rs:1:1 + | +LL | struct FancyNum { + | ^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/error-codes/E0505.stderr b/tests/ui/error-codes/E0505.stderr index 250680d2c1c..ce01298a70d 100644 --- a/tests/ui/error-codes/E0505.stderr +++ b/tests/ui/error-codes/E0505.stderr @@ -10,6 +10,12 @@ LL | eat(x); | ^ move out of `x` occurs here LL | _ref_to_val.use_ref(); | ----------- borrow later used here + | +note: if `Value` implemented `Clone`, you could clone the value + --> $DIR/E0505.rs:1:1 + | +LL | struct Value {} + | ^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/error-codes/E0507.stderr b/tests/ui/error-codes/E0507.stderr index 767fedfccbf..60a4daa9d38 100644 --- a/tests/ui/error-codes/E0507.stderr +++ b/tests/ui/error-codes/E0507.stderr @@ -11,6 +11,11 @@ note: `TheDarkKnight::nothing_is_true` takes ownership of the receiver `self`, w | LL | fn nothing_is_true(self) {} | ^^^^ +note: if `TheDarkKnight` implemented `Clone`, you could clone the value + --> $DIR/E0507.rs:3:1 + | +LL | struct TheDarkKnight; + | ^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/error-codes/E0508-fail.stderr b/tests/ui/error-codes/E0508-fail.stderr index 1153b1d09c7..96d3bcb67a5 100644 --- a/tests/ui/error-codes/E0508-fail.stderr +++ b/tests/ui/error-codes/E0508-fail.stderr @@ -7,6 +7,11 @@ LL | let _value = array[0]; | cannot move out of here | move occurs because `array[_]` has type `NonCopy`, which does not implement the `Copy` trait | +note: if `NonCopy` implemented `Clone`, you could clone the value + --> $DIR/E0508-fail.rs:1:1 + | +LL | struct NonCopy; + | ^^^^^^^^^^^^^^ help: consider borrowing here | LL | let _value = &array[0]; diff --git a/tests/ui/error-codes/E0508.stderr b/tests/ui/error-codes/E0508.stderr index 4c864e24144..c1b622e2432 100644 --- a/tests/ui/error-codes/E0508.stderr +++ b/tests/ui/error-codes/E0508.stderr @@ -7,6 +7,11 @@ LL | let _value = array[0]; | cannot move out of here | move occurs because `array[_]` has type `NonCopy`, which does not implement the `Copy` trait | +note: if `NonCopy` implemented `Clone`, you could clone the value + --> $DIR/E0508.rs:1:1 + | +LL | struct NonCopy; + | ^^^^^^^^^^^^^^ help: consider borrowing here | LL | let _value = &array[0]; diff --git a/tests/ui/error-codes/E0509.stderr b/tests/ui/error-codes/E0509.stderr index 59843a5491a..75c372d0440 100644 --- a/tests/ui/error-codes/E0509.stderr +++ b/tests/ui/error-codes/E0509.stderr @@ -7,6 +7,11 @@ LL | let fancy_field = drop_struct.fancy; | cannot move out of here | move occurs because `drop_struct.fancy` has type `FancyNum`, which does not implement the `Copy` trait | +note: if `FancyNum` implemented `Clone`, you could clone the value + --> $DIR/E0509.rs:1:1 + | +LL | struct FancyNum { + | ^^^^^^^^^^^^^^^ help: consider borrowing here | LL | let fancy_field = &drop_struct.fancy; diff --git a/tests/ui/fmt/send-sync.stderr b/tests/ui/fmt/send-sync.stderr index aa377553c50..bebf575d9a7 100644 --- a/tests/ui/fmt/send-sync.stderr +++ b/tests/ui/fmt/send-sync.stderr @@ -8,6 +8,8 @@ LL | send(format_args!("{:?}", c)); | = help: within `[core::fmt::rt::Argument<'_>]`, the trait `Sync` is not implemented for `core::fmt::rt::Opaque`, which is required by `Arguments<'_>: Send` = note: required because it appears within the type `&core::fmt::rt::Opaque` +note: required because it appears within the type `core::fmt::rt::ArgumentType<'_>` + --> $SRC_DIR/core/src/fmt/rt.rs:LL:COL note: required because it appears within the type `core::fmt::rt::Argument<'_>` --> $SRC_DIR/core/src/fmt/rt.rs:LL:COL = note: required because it appears within the type `[core::fmt::rt::Argument<'_>]` @@ -30,6 +32,8 @@ LL | sync(format_args!("{:?}", c)); | = help: within `Arguments<'_>`, the trait `Sync` is not implemented for `core::fmt::rt::Opaque`, which is required by `Arguments<'_>: Sync` = note: required because it appears within the type `&core::fmt::rt::Opaque` +note: required because it appears within the type `core::fmt::rt::ArgumentType<'_>` + --> $SRC_DIR/core/src/fmt/rt.rs:LL:COL note: required because it appears within the type `core::fmt::rt::Argument<'_>` --> $SRC_DIR/core/src/fmt/rt.rs:LL:COL = note: required because it appears within the type `[core::fmt::rt::Argument<'_>]` diff --git a/tests/ui/fn/implied-bounds-unnorm-associated-type-4.stderr b/tests/ui/fn/implied-bounds-unnorm-associated-type-4.stderr index 3be630e2b23..b8ec2e3b7e7 100644 --- a/tests/ui/fn/implied-bounds-unnorm-associated-type-4.stderr +++ b/tests/ui/fn/implied-bounds-unnorm-associated-type-4.stderr @@ -10,6 +10,12 @@ LL | drop(x); LL | LL | println!("{}", y); | - borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL - let y = f(&x, ()); +LL + let y = f(x.clone(), ()); + | error: aborting due to 1 previous error diff --git a/tests/ui/fn/implied-bounds-unnorm-associated-type-5.stderr b/tests/ui/fn/implied-bounds-unnorm-associated-type-5.stderr index bf6d77b6269..382ab8636a2 100644 --- a/tests/ui/fn/implied-bounds-unnorm-associated-type-5.stderr +++ b/tests/ui/fn/implied-bounds-unnorm-associated-type-5.stderr @@ -27,6 +27,12 @@ LL | drop(x); | ^ move out of `x` occurs here LL | println!("{}", y); | - borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL - let y = f(&x, ()); +LL + let y = f(x.clone(), ()); + | error: aborting due to 2 previous errors diff --git a/tests/ui/fn/implied-bounds-unnorm-associated-type.stderr b/tests/ui/fn/implied-bounds-unnorm-associated-type.stderr index c2a8fa741ca..ce97d8527e8 100644 --- a/tests/ui/fn/implied-bounds-unnorm-associated-type.stderr +++ b/tests/ui/fn/implied-bounds-unnorm-associated-type.stderr @@ -10,6 +10,12 @@ LL | drop(x); LL | LL | println!("{}", y); | - borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL - let y = f(&x, ()); +LL + let y = f(x.clone(), ()); + | error: aborting due to 1 previous error diff --git a/tests/ui/functional-struct-update/functional-struct-update-noncopyable.stderr b/tests/ui/functional-struct-update/functional-struct-update-noncopyable.stderr index 16808f29dac..d167a60dad3 100644 --- a/tests/ui/functional-struct-update/functional-struct-update-noncopyable.stderr +++ b/tests/ui/functional-struct-update/functional-struct-update-noncopyable.stderr @@ -6,6 +6,11 @@ LL | let _b = A { y: Arc::new(3), ..a }; | | | cannot move out of here | move occurs because `a.x` has type `Arc<isize>`, which does not implement the `Copy` trait + | +help: clone the value from the field instead of using the functional record update syntax + | +LL | let _b = A { y: Arc::new(3), x: a.x.clone() }; + | ~~~~~~~~~~~~~~~~ error: aborting due to 1 previous error diff --git a/tests/ui/generic-const-items/inference-failure.stderr b/tests/ui/generic-const-items/inference-failure.stderr index 10ecd83ec53..594743a47f4 100644 --- a/tests/ui/generic-const-items/inference-failure.stderr +++ b/tests/ui/generic-const-items/inference-failure.stderr @@ -1,4 +1,4 @@ -error[E0282]: type annotations needed for `Option<T>` +error[E0282]: type annotations needed for `Option<_>` --> $DIR/inference-failure.rs:8:9 | LL | let _ = NONE; diff --git a/tests/ui/generic-const-items/parameter-defaults.stderr b/tests/ui/generic-const-items/parameter-defaults.stderr index b8220af5d0e..13562c98f6d 100644 --- a/tests/ui/generic-const-items/parameter-defaults.stderr +++ b/tests/ui/generic-const-items/parameter-defaults.stderr @@ -4,7 +4,7 @@ error: defaults for type parameters are only allowed in `struct`, `enum`, `type` LL | const NONE<T = ()>: Option<T> = None::<T>; | ^^^^^^ -error[E0282]: type annotations needed for `Option<T>` +error[E0282]: type annotations needed for `Option<_>` --> $DIR/parameter-defaults.rs:13:9 | LL | let _ = NONE; diff --git a/tests/ui/half-open-range-patterns/half-open-range-pats-inclusive-dotdotdot-bad-syntax.stderr b/tests/ui/half-open-range-patterns/half-open-range-pats-inclusive-dotdotdot-bad-syntax.stderr index 6832f21f25e..0d2aae689f0 100644 --- a/tests/ui/half-open-range-patterns/half-open-range-pats-inclusive-dotdotdot-bad-syntax.stderr +++ b/tests/ui/half-open-range-patterns/half-open-range-pats-inclusive-dotdotdot-bad-syntax.stderr @@ -46,10 +46,6 @@ LL | mac!(0); = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html = note: the matched value is of type `i32` = note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info) -help: you might want to use `if let` to ignore the variant that isn't matched - | -LL | if let ...$e; { todo!() } - | ++ +++++++++++ error: aborting due to 6 previous errors diff --git a/tests/ui/half-open-range-patterns/half-open-range-pats-inclusive-no-end.stderr b/tests/ui/half-open-range-patterns/half-open-range-pats-inclusive-no-end.stderr index cb9e48e70e3..9ba0e09e154 100644 --- a/tests/ui/half-open-range-patterns/half-open-range-pats-inclusive-no-end.stderr +++ b/tests/ui/half-open-range-patterns/half-open-range-pats-inclusive-no-end.stderr @@ -67,10 +67,6 @@ LL | mac!(0); = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html = note: the matched value is of type `i32` = note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info) -help: you might want to use `if let` to ignore the variant that isn't matched - | -LL | if let $e...; { todo!() } - | ++ +++++++++++ error[E0005]: refutable pattern in local binding --> $DIR/half-open-range-pats-inclusive-no-end.rs:20:17 @@ -85,10 +81,6 @@ LL | mac!(0); = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html = note: the matched value is of type `i32` = note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info) -help: you might want to use `if let` to ignore the variant that isn't matched - | -LL | if let $e..=; { todo!() } - | ++ +++++++++++ error: aborting due to 8 previous errors diff --git a/tests/ui/imports/redundant-import-extern-prelude.rs b/tests/ui/imports/redundant-import-extern-prelude.rs new file mode 100644 index 00000000000..f1de06417aa --- /dev/null +++ b/tests/ui/imports/redundant-import-extern-prelude.rs @@ -0,0 +1,16 @@ +//@ check-pass +// Check that we detect imports that are redundant due to the extern prelude +// and that we emit a reasonable diagnostic. +// issue: rust-lang/rust#121915 + +// See also the discussion in <https://github.com/rust-lang/rust/pull/122954>. + +//@ compile-flags: --extern aux_issue_121915 --edition 2018 +//@ aux-build: aux-issue-121915.rs + +#[deny(unused_imports)] +fn main() { + use aux_issue_121915; + //FIXME(unused_imports): ~^ ERROR the item `aux_issue_121915` is imported redundantly + aux_issue_121915::item(); +} diff --git a/tests/ui/imports/redundant-import-issue-121915-2015.rs b/tests/ui/imports/redundant-import-issue-121915-2015.rs index d41d190bb58..be3b8209ada 100644 --- a/tests/ui/imports/redundant-import-issue-121915-2015.rs +++ b/tests/ui/imports/redundant-import-issue-121915-2015.rs @@ -1,3 +1,4 @@ +//@ check-pass //@ compile-flags: --extern aux_issue_121915 --edition 2015 //@ aux-build: aux-issue-121915.rs @@ -6,6 +7,6 @@ extern crate aux_issue_121915; #[deny(unused_imports)] fn main() { use aux_issue_121915; - //~^ ERROR the item `aux_issue_121915` is imported redundantly + //FIXME(unused_imports): ~^ ERROR the item `aux_issue_121915` is imported redundantly aux_issue_121915::item(); } diff --git a/tests/ui/imports/redundant-import-issue-121915-2015.stderr b/tests/ui/imports/redundant-import-issue-121915-2015.stderr deleted file mode 100644 index 174ed4fb96b..00000000000 --- a/tests/ui/imports/redundant-import-issue-121915-2015.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error: the item `aux_issue_121915` is imported redundantly - --> $DIR/redundant-import-issue-121915-2015.rs:8:9 - | -LL | extern crate aux_issue_121915; - | ------------------------------ the item `aux_issue_121915` is already imported here -... -LL | use aux_issue_121915; - | ^^^^^^^^^^^^^^^^ - | -note: the lint level is defined here - --> $DIR/redundant-import-issue-121915-2015.rs:6:8 - | -LL | #[deny(unused_imports)] - | ^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/imports/redundant-import-issue-121915.rs b/tests/ui/imports/redundant-import-issue-121915.rs deleted file mode 100644 index 237acc4af25..00000000000 --- a/tests/ui/imports/redundant-import-issue-121915.rs +++ /dev/null @@ -1,9 +0,0 @@ -//@ compile-flags: --extern aux_issue_121915 --edition 2018 -//@ aux-build: aux-issue-121915.rs - -#[deny(unused_imports)] -fn main() { - use aux_issue_121915; - //~^ ERROR the item `aux_issue_121915` is imported redundantly - aux_issue_121915::item(); -} diff --git a/tests/ui/imports/redundant-import-issue-121915.stderr b/tests/ui/imports/redundant-import-issue-121915.stderr deleted file mode 100644 index 0047d7c3420..00000000000 --- a/tests/ui/imports/redundant-import-issue-121915.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: the item `aux_issue_121915` is imported redundantly - --> $DIR/redundant-import-issue-121915.rs:6:9 - | -LL | use aux_issue_121915; - | ^^^^^^^^^^^^^^^^ the item `aux_issue_121915` is already defined by prelude - | -note: the lint level is defined here - --> $DIR/redundant-import-issue-121915.rs:4:8 - | -LL | #[deny(unused_imports)] - | ^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/imports/redundant-import-lang-prelude-attr.rs b/tests/ui/imports/redundant-import-lang-prelude-attr.rs new file mode 100644 index 00000000000..59cd570f44c --- /dev/null +++ b/tests/ui/imports/redundant-import-lang-prelude-attr.rs @@ -0,0 +1,17 @@ +//@ check-pass +// Check that we detect imports (of built-in attributes) that are redundant due to +// the language prelude and that we emit a reasonable diagnostic. + +// Note that we use the term "extern prelude" in the label even though "language prelude" +// would be more correct. However, it's not worth special-casing this. + +// See also the discussion in <https://github.com/rust-lang/rust/pull/122954>. + +//@ edition: 2018 + +#![deny(unused_imports)] + +use allow; //FIXME(unused_imports): ~ ERROR the item `allow` is imported redundantly + +#[allow(unused)] +fn main() {} diff --git a/tests/ui/imports/redundant-import-lang-prelude.rs b/tests/ui/imports/redundant-import-lang-prelude.rs new file mode 100644 index 00000000000..53d3b709963 --- /dev/null +++ b/tests/ui/imports/redundant-import-lang-prelude.rs @@ -0,0 +1,17 @@ +//@ check-pass +// Check that we detect imports that are redundant due to the language prelude +// and that we emit a reasonable diagnostic. + +// Note that we use the term "extern prelude" in the label even though "language prelude" +// would be more correct. However, it's not worth special-casing this. + +// See also the discussion in <https://github.com/rust-lang/rust/pull/122954>. + +#![deny(unused_imports)] + +use std::primitive::u8; +//FIXME(unused_imports): ~^ ERROR the item `u8` is imported redundantly + +const _: u8 = 0; + +fn main() {} diff --git a/tests/ui/imports/redundant-import-undetected-macro-use-prelude.rs b/tests/ui/imports/redundant-import-undetected-macro-use-prelude.rs new file mode 100644 index 00000000000..4a79cb2c208 --- /dev/null +++ b/tests/ui/imports/redundant-import-undetected-macro-use-prelude.rs @@ -0,0 +1,23 @@ +// This test demonstrates that we currently don't make an effort to detect +// imports made redundant by the `#[macro_use]` prelude. +// See also the discussion in <https://github.com/rust-lang/rust/pull/122954>. + +//@ check-pass +//@ aux-build:two_macros.rs +#![deny(unused_imports)] + +#[macro_use] +extern crate two_macros; + +// This import is actually redundant due to the `#[macro_use]` above. +use two_macros::n; + +// We intentionally reference two items from the `#[macro_use]`'d crate because +// if we were to reference only item `n`, we would flag the `#[macro_use]` +// attribute as redundant which would be correct of course. +// That's interesting on its own -- we prefer "blaming" the `#[macro_use]` +// over the import (here, `use two_macros::n`) when it comes to redundancy. +n!(); +m!(); + +fn main() {} diff --git a/tests/ui/imports/suggest-remove-issue-121315.rs b/tests/ui/imports/suggest-remove-issue-121315.rs index 63533480ec1..2bb82833a5b 100644 --- a/tests/ui/imports/suggest-remove-issue-121315.rs +++ b/tests/ui/imports/suggest-remove-issue-121315.rs @@ -1,19 +1,20 @@ //@ compile-flags: --edition 2021 + #![deny(unused_imports)] #![allow(dead_code)] fn test0() { // Test remove FlatUnused use std::convert::TryFrom; - //~^ ERROR the item `TryFrom` is imported redundantly + //FIXME(unused_imports): ~^ ERROR the item `TryFrom` is imported redundantly let _ = u32::try_from(5i32); } fn test1() { // FIXME(yukang) Test remove NestedFullUnused use std::convert::{TryFrom, TryInto}; - //~^ ERROR the item `TryFrom` is imported redundantly - //~| ERROR the item `TryInto` is imported redundantly + //FIXME(unused_imports): ~^ ERROR the item `TryFrom` is imported redundantly + //FIXME(unused_imports): ~| ERROR the item `TryInto` is imported redundantly let _ = u32::try_from(5i32); let _a: i32 = u32::try_into(5u32).unwrap(); @@ -23,7 +24,7 @@ fn test2() { // FIXME(yukang): Test remove both redundant and unused use std::convert::{AsMut, Into}; //~^ ERROR unused import: `AsMut` - //~| ERROR the item `Into` is imported redundantly + //FIXME(unused_imports): ~| ERROR the item `Into` is imported redundantly let _a: u32 = (5u8).into(); } diff --git a/tests/ui/imports/suggest-remove-issue-121315.stderr b/tests/ui/imports/suggest-remove-issue-121315.stderr index dbd742f6c78..5701514e1bd 100644 --- a/tests/ui/imports/suggest-remove-issue-121315.stderr +++ b/tests/ui/imports/suggest-remove-issue-121315.stderr @@ -1,56 +1,20 @@ -error: the item `TryFrom` is imported redundantly - --> $DIR/suggest-remove-issue-121315.rs:7:9 - | -LL | use std::convert::TryFrom; - | ^^^^^^^^^^^^^^^^^^^^^ - --> $SRC_DIR/std/src/prelude/mod.rs:LL:COL - | - = note: the item `TryFrom` is already defined here - | -note: the lint level is defined here - --> $DIR/suggest-remove-issue-121315.rs:2:9 - | -LL | #![deny(unused_imports)] - | ^^^^^^^^^^^^^^ - -error: the item `TryFrom` is imported redundantly - --> $DIR/suggest-remove-issue-121315.rs:14:24 - | -LL | use std::convert::{TryFrom, TryInto}; - | ^^^^^^^ - --> $SRC_DIR/std/src/prelude/mod.rs:LL:COL - | - = note: the item `TryFrom` is already defined here - -error: the item `TryInto` is imported redundantly - --> $DIR/suggest-remove-issue-121315.rs:14:33 - | -LL | use std::convert::{TryFrom, TryInto}; - | ^^^^^^^ - --> $SRC_DIR/std/src/prelude/mod.rs:LL:COL - | - = note: the item `TryInto` is already defined here - error: unused import: `AsMut` - --> $DIR/suggest-remove-issue-121315.rs:24:24 + --> $DIR/suggest-remove-issue-121315.rs:25:24 | LL | use std::convert::{AsMut, Into}; | ^^^^^ - -error: the item `Into` is imported redundantly - --> $DIR/suggest-remove-issue-121315.rs:24:31 | -LL | use std::convert::{AsMut, Into}; - | ^^^^ - --> $SRC_DIR/std/src/prelude/mod.rs:LL:COL +note: the lint level is defined here + --> $DIR/suggest-remove-issue-121315.rs:3:9 | - = note: the item `Into` is already defined here +LL | #![deny(unused_imports)] + | ^^^^^^^^^^^^^^ error: unused import: `From` - --> $DIR/suggest-remove-issue-121315.rs:33:24 + --> $DIR/suggest-remove-issue-121315.rs:34:24 | LL | use std::convert::{From, Infallible}; | ^^^^ -error: aborting due to 6 previous errors +error: aborting due to 2 previous errors diff --git a/tests/ui/include-macros/mismatched-types.stderr b/tests/ui/include-macros/mismatched-types.stderr index 4f2880e2f5d..9bc0e64464e 100644 --- a/tests/ui/include-macros/mismatched-types.stderr +++ b/tests/ui/include-macros/mismatched-types.stderr @@ -1,8 +1,11 @@ error[E0308]: mismatched types - --> $DIR/mismatched-types.rs:2:20 + --> $DIR/file.txt:0:1 + | + | + ::: $DIR/mismatched-types.rs:2:12 | LL | let b: &[u8] = include_str!("file.txt"); - | ----- ^^^^^^^^^^^^^^^^^^^^^^^^ expected `&[u8]`, found `&str` + | ----- ------------------------ in this macro invocation | | | expected due to this | diff --git a/tests/ui/inference/cannot-infer-closure-circular.rs b/tests/ui/inference/cannot-infer-closure-circular.rs index affb481496d..1b41171e74a 100644 --- a/tests/ui/inference/cannot-infer-closure-circular.rs +++ b/tests/ui/inference/cannot-infer-closure-circular.rs @@ -4,7 +4,7 @@ fn main() { // error handles this gracefully, and in particular doesn't generate an extra // note about the `?` operator in the closure body, which isn't relevant to // the inference. - let x = |r| { //~ ERROR type annotations needed for `Result<(), E>` + let x = |r| { //~ ERROR type annotations needed for `Result<(), _>` let v = r?; Ok(v) }; diff --git a/tests/ui/inference/cannot-infer-closure-circular.stderr b/tests/ui/inference/cannot-infer-closure-circular.stderr index e3cf0cca837..a16e832f8ef 100644 --- a/tests/ui/inference/cannot-infer-closure-circular.stderr +++ b/tests/ui/inference/cannot-infer-closure-circular.stderr @@ -1,4 +1,4 @@ -error[E0282]: type annotations needed for `Result<(), E>` +error[E0282]: type annotations needed for `Result<(), _>` --> $DIR/cannot-infer-closure-circular.rs:7:14 | LL | let x = |r| { diff --git a/tests/ui/inference/erase-type-params-in-label.stderr b/tests/ui/inference/erase-type-params-in-label.stderr index 546e679f2d0..4e9a74c1e40 100644 --- a/tests/ui/inference/erase-type-params-in-label.stderr +++ b/tests/ui/inference/erase-type-params-in-label.stderr @@ -1,4 +1,4 @@ -error[E0283]: type annotations needed for `Foo<i32, &str, W, Z>` +error[E0283]: type annotations needed for `Foo<i32, &str, _, _>` --> $DIR/erase-type-params-in-label.rs:2:9 | LL | let foo = foo(1, ""); @@ -15,7 +15,7 @@ help: consider giving `foo` an explicit type, where the type for type parameter LL | let foo: Foo<i32, &str, W, Z> = foo(1, ""); | ++++++++++++++++++++++ -error[E0283]: type annotations needed for `Bar<i32, &str, Z>` +error[E0283]: type annotations needed for `Bar<i32, &str, _>` --> $DIR/erase-type-params-in-label.rs:5:9 | LL | let bar = bar(1, ""); diff --git a/tests/ui/inference/issue-104649.stderr b/tests/ui/inference/issue-104649.stderr index afece960914..391ed16f349 100644 --- a/tests/ui/inference/issue-104649.stderr +++ b/tests/ui/inference/issue-104649.stderr @@ -1,4 +1,4 @@ -error[E0282]: type annotations needed for `A<std::result::Result<std::result::Result<(), E>, Error>>` +error[E0282]: type annotations needed for `A<std::result::Result<std::result::Result<(), _>, Error>>` --> $DIR/issue-104649.rs:24:9 | LL | let a = A(Result::Ok(Result::Ok(()))); diff --git a/tests/ui/inference/issue-72690.stderr b/tests/ui/inference/issue-72690.stderr index 6c93241ea07..6391672f861 100644 --- a/tests/ui/inference/issue-72690.stderr +++ b/tests/ui/inference/issue-72690.stderr @@ -50,7 +50,7 @@ help: try using a fully qualified path to specify the expected types LL | |x| String::from(<str as AsRef<T>>::as_ref("x")); | ++++++++++++++++++++++++++ ~ -error[E0283]: type annotations needed for `&T` +error[E0283]: type annotations needed for `&_` --> $DIR/issue-72690.rs:17:9 | LL | let _ = "x".as_ref(); diff --git a/tests/ui/inference/issue-83606.rs b/tests/ui/inference/issue-83606.rs index c387046e910..4454b5e60f0 100644 --- a/tests/ui/inference/issue-83606.rs +++ b/tests/ui/inference/issue-83606.rs @@ -6,5 +6,5 @@ fn foo<const N: usize>(_: impl std::fmt::Display) -> [usize; N] { fn main() { let _ = foo("foo"); - //~^ ERROR: type annotations needed for `[usize; N]` + //~^ ERROR type annotations needed for `[usize; _]` } diff --git a/tests/ui/inference/issue-83606.stderr b/tests/ui/inference/issue-83606.stderr index 00de4029e42..8e6ff6d568d 100644 --- a/tests/ui/inference/issue-83606.stderr +++ b/tests/ui/inference/issue-83606.stderr @@ -1,4 +1,4 @@ -error[E0282]: type annotations needed for `[usize; N]` +error[E0282]: type annotations needed for `[usize; _]` --> $DIR/issue-83606.rs:8:9 | LL | let _ = foo("foo"); diff --git a/tests/ui/inference/untyped-primitives.rs b/tests/ui/inference/untyped-primitives.rs new file mode 100644 index 00000000000..8515ca79903 --- /dev/null +++ b/tests/ui/inference/untyped-primitives.rs @@ -0,0 +1,9 @@ +//@ check-pass +// issue: rust-lang/rust#123824 +// This test is a sanity check and does not enforce any stable API, so may be +// removed at a future point. + +fn main() { + let x = f32::from(3.14); + let y = f64::from(3.14); +} diff --git a/tests/ui/issues/issue-11771.stderr b/tests/ui/issues/issue-11771.stderr index 161fce4b031..d4a4647f6a1 100644 --- a/tests/ui/issues/issue-11771.stderr +++ b/tests/ui/issues/issue-11771.stderr @@ -6,15 +6,15 @@ LL | 1 + | = help: the trait `Add<()>` is not implemented for `{integer}` = help: the following other types implement trait `Add<Rhs>`: + <&'a f128 as Add<f128>> + <&'a f16 as Add<f16>> <&'a f32 as Add<f32>> <&'a f64 as Add<f64>> <&'a i128 as Add<i128>> <&'a i16 as Add<i16>> <&'a i32 as Add<i32>> <&'a i64 as Add<i64>> - <&'a i8 as Add<i8>> - <&'a isize as Add<isize>> - and 48 others + and 56 others error[E0277]: cannot add `()` to `{integer}` --> $DIR/issue-11771.rs:8:7 @@ -24,15 +24,15 @@ LL | 1 + | = help: the trait `Add<()>` is not implemented for `{integer}` = help: the following other types implement trait `Add<Rhs>`: + <&'a f128 as Add<f128>> + <&'a f16 as Add<f16>> <&'a f32 as Add<f32>> <&'a f64 as Add<f64>> <&'a i128 as Add<i128>> <&'a i16 as Add<i16>> <&'a i32 as Add<i32>> <&'a i64 as Add<i64>> - <&'a i8 as Add<i8>> - <&'a isize as Add<isize>> - and 48 others + and 56 others error: aborting due to 2 previous errors diff --git a/tests/ui/issues/issue-12187-1.stderr b/tests/ui/issues/issue-12187-1.stderr index 93dc1df8f63..704854fe585 100644 --- a/tests/ui/issues/issue-12187-1.stderr +++ b/tests/ui/issues/issue-12187-1.stderr @@ -1,4 +1,4 @@ -error[E0282]: type annotations needed for `&T` +error[E0282]: type annotations needed for `&_` --> $DIR/issue-12187-1.rs:6:9 | LL | let &v = new(); diff --git a/tests/ui/issues/issue-12187-2.stderr b/tests/ui/issues/issue-12187-2.stderr index e9ba52ff4fd..eeef63a1d0b 100644 --- a/tests/ui/issues/issue-12187-2.stderr +++ b/tests/ui/issues/issue-12187-2.stderr @@ -1,4 +1,4 @@ -error[E0282]: type annotations needed for `&T` +error[E0282]: type annotations needed for `&_` --> $DIR/issue-12187-2.rs:6:9 | LL | let &v = new(); diff --git a/tests/ui/issues/issue-17385.stderr b/tests/ui/issues/issue-17385.stderr index 77aa201b335..988db0fb1fc 100644 --- a/tests/ui/issues/issue-17385.stderr +++ b/tests/ui/issues/issue-17385.stderr @@ -7,6 +7,12 @@ LL | drop(foo); | --- value moved here LL | match foo { | ^^^^^^^^^ value used here after move + | +note: if `X` implemented `Clone`, you could clone the value + --> $DIR/issue-17385.rs:1:1 + | +LL | struct X(isize); + | ^^^^^^^^ error[E0382]: use of moved value: `e` --> $DIR/issue-17385.rs:25:11 @@ -17,6 +23,12 @@ LL | drop(e); | - value moved here LL | match e { | ^ value used here after move + | +note: if `Enum` implemented `Clone`, you could clone the value + --> $DIR/issue-17385.rs:3:1 + | +LL | enum Enum { + | ^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/tests/ui/issues/issue-17551.stderr b/tests/ui/issues/issue-17551.stderr index 68f54a31084..b9cb76fc298 100644 --- a/tests/ui/issues/issue-17551.stderr +++ b/tests/ui/issues/issue-17551.stderr @@ -1,4 +1,4 @@ -error[E0282]: type annotations needed for `B<T>` +error[E0282]: type annotations needed for `B<_>` --> $DIR/issue-17551.rs:6:9 | LL | let foo = B(marker::PhantomData); diff --git a/tests/ui/issues/issue-21596.rs b/tests/ui/issues/issue-21596.rs deleted file mode 100644 index 79f6c91d9ac..00000000000 --- a/tests/ui/issues/issue-21596.rs +++ /dev/null @@ -1,5 +0,0 @@ -fn main() { - let x = 8u8; - let z: *const u8 = &x; - println!("{}", z.to_string()); //~ ERROR E0599 -} diff --git a/tests/ui/issues/issue-21596.stderr b/tests/ui/issues/issue-21596.stderr deleted file mode 100644 index 8a7fca5f436..00000000000 --- a/tests/ui/issues/issue-21596.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error[E0599]: `*const u8` doesn't implement `std::fmt::Display` - --> $DIR/issue-21596.rs:4:22 - | -LL | println!("{}", z.to_string()); - | ^^^^^^^^^ `*const u8` cannot be formatted with the default formatter - | - = note: try using `<*const T>::as_ref()` to get a reference to the type behind the pointer: https://doc.rust-lang.org/std/primitive.pointer.html#method.as_ref - = note: using `<*const T>::as_ref()` on a pointer which is unaligned or points to invalid or uninitialized memory is undefined behavior - = note: the following trait bounds were not satisfied: - `*const u8: std::fmt::Display` - which is required by `*const u8: ToString` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/issues/issue-23046.stderr b/tests/ui/issues/issue-23046.stderr index b6e23814543..f70ac0c9f38 100644 --- a/tests/ui/issues/issue-23046.stderr +++ b/tests/ui/issues/issue-23046.stderr @@ -1,4 +1,4 @@ -error[E0282]: type annotations needed for `Expr<'_, VAR>` +error[E0282]: type annotations needed for `Expr<'_, _>` --> $DIR/issue-23046.rs:17:15 | LL | let ex = |x| { diff --git a/tests/ui/issues/issue-24357.rs b/tests/ui/issues/issue-24357.rs index 152e69ebc87..d1a9e37251e 100644 --- a/tests/ui/issues/issue-24357.rs +++ b/tests/ui/issues/issue-24357.rs @@ -1,4 +1,4 @@ -struct NoCopy; +struct NoCopy; //~ NOTE if `NoCopy` implemented `Clone`, you could clone the value fn main() { let x = NoCopy; //~^ NOTE move occurs because `x` has type `NoCopy` diff --git a/tests/ui/issues/issue-24357.stderr b/tests/ui/issues/issue-24357.stderr index 08a5a8ac56e..6d50eea7e21 100644 --- a/tests/ui/issues/issue-24357.stderr +++ b/tests/ui/issues/issue-24357.stderr @@ -11,6 +11,12 @@ LL | let f = move || { let y = x; }; ... LL | let z = x; | ^ value used here after move + | +note: if `NoCopy` implemented `Clone`, you could clone the value + --> $DIR/issue-24357.rs:1:1 + | +LL | struct NoCopy; + | ^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-2590.stderr b/tests/ui/issues/issue-2590.stderr index 517b4814eae..822856652e9 100644 --- a/tests/ui/issues/issue-2590.stderr +++ b/tests/ui/issues/issue-2590.stderr @@ -3,6 +3,11 @@ error[E0507]: cannot move out of `self.tokens` which is behind a shared referenc | LL | self.tokens | ^^^^^^^^^^^ move occurs because `self.tokens` has type `Vec<isize>`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL | self.tokens.clone() + | ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-27042.stderr b/tests/ui/issues/issue-27042.stderr index 01532de999e..ba39399e46e 100644 --- a/tests/ui/issues/issue-27042.stderr +++ b/tests/ui/issues/issue-27042.stderr @@ -19,7 +19,7 @@ LL | loop { break }; | | | this loop is expected to be of type `i32` | -help: give it a value of the expected type +help: give the `break` a value of the expected type | LL | loop { break 42 }; | ++ diff --git a/tests/ui/issues/issue-28433.stderr b/tests/ui/issues/issue-28433.stderr index 5fb8a89621c..0fa67e35f1d 100644 --- a/tests/ui/issues/issue-28433.stderr +++ b/tests/ui/issues/issue-28433.stderr @@ -2,7 +2,7 @@ error[E0449]: visibility qualifiers are not permitted here --> $DIR/issue-28433.rs:2:5 | LL | pub Duck, - | ^^^ + | ^^^ help: remove the qualifier | = note: enum variants and their fields always share the visibility of the enum they are in @@ -10,7 +10,7 @@ error[E0449]: visibility qualifiers are not permitted here --> $DIR/issue-28433.rs:5:5 | LL | pub(crate) Dove - | ^^^^^^^^^^ + | ^^^^^^^^^^ help: remove the qualifier | = note: enum variants and their fields always share the visibility of the enum they are in diff --git a/tests/ui/issues/issue-40402-ref-hints/issue-40402-1.stderr b/tests/ui/issues/issue-40402-ref-hints/issue-40402-1.stderr index 7976d090542..d27b6e6324f 100644 --- a/tests/ui/issues/issue-40402-ref-hints/issue-40402-1.stderr +++ b/tests/ui/issues/issue-40402-ref-hints/issue-40402-1.stderr @@ -8,6 +8,10 @@ help: consider borrowing here | LL | let e = &f.v[0]; | + +help: consider cloning the value if the performance cost is acceptable + | +LL | let e = f.v[0].clone(); + | ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-50582.stderr b/tests/ui/issues/issue-50582.stderr index 1967b51128f..b765d2f087d 100644 --- a/tests/ui/issues/issue-50582.stderr +++ b/tests/ui/issues/issue-50582.stderr @@ -16,15 +16,15 @@ LL | Vec::<[(); 1 + for x in 0..1 {}]>::new(); | = help: the trait `Add<()>` is not implemented for `{integer}` = help: the following other types implement trait `Add<Rhs>`: + <&'a f128 as Add<f128>> + <&'a f16 as Add<f16>> <&'a f32 as Add<f32>> <&'a f64 as Add<f64>> <&'a i128 as Add<i128>> <&'a i16 as Add<i16>> <&'a i32 as Add<i32>> <&'a i64 as Add<i64>> - <&'a i8 as Add<i8>> - <&'a isize as Add<isize>> - and 48 others + and 56 others error: aborting due to 2 previous errors diff --git a/tests/ui/issues/issue-52262.stderr b/tests/ui/issues/issue-52262.stderr index ce8e6fe2bf8..51959f22b97 100644 --- a/tests/ui/issues/issue-52262.stderr +++ b/tests/ui/issues/issue-52262.stderr @@ -3,6 +3,12 @@ error[E0507]: cannot move out of `*key` which is behind a shared reference | LL | String::from_utf8(*key).unwrap() | ^^^^ move occurs because `*key` has type `Vec<u8>`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL - String::from_utf8(*key).unwrap() +LL + String::from_utf8(key.clone()).unwrap() + | error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-98299.stderr b/tests/ui/issues/issue-98299.stderr index e99d8e5cc80..becf16d1db9 100644 --- a/tests/ui/issues/issue-98299.stderr +++ b/tests/ui/issues/issue-98299.stderr @@ -1,4 +1,4 @@ -error[E0282]: type annotations needed for `SmallCString<N>` +error[E0282]: type annotations needed for `SmallCString<_>` --> $DIR/issue-98299.rs:4:36 | LL | SmallCString::try_from(p).map(|cstr| cstr); diff --git a/tests/ui/let-else/uninitialized-refutable-let-issue-123844.rs b/tests/ui/let-else/uninitialized-refutable-let-issue-123844.rs new file mode 100644 index 00000000000..8426b0021f4 --- /dev/null +++ b/tests/ui/let-else/uninitialized-refutable-let-issue-123844.rs @@ -0,0 +1,8 @@ +// https://github.com/rust-lang/rust/issues/123844 +// An uninitialized refutable let should not suggest `let else`, as it can't be used with deferred +// initialization. + +fn main() { + let Some(x); //~ ERROR refutable pattern in local binding + x = 1; +} diff --git a/tests/ui/let-else/uninitialized-refutable-let-issue-123844.stderr b/tests/ui/let-else/uninitialized-refutable-let-issue-123844.stderr new file mode 100644 index 00000000000..13312306c07 --- /dev/null +++ b/tests/ui/let-else/uninitialized-refutable-let-issue-123844.stderr @@ -0,0 +1,13 @@ +error[E0005]: refutable pattern in local binding + --> $DIR/uninitialized-refutable-let-issue-123844.rs:6:9 + | +LL | let Some(x); + | ^^^^^^^ pattern `None` not covered + | + = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant + = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html + = note: the matched value is of type `Option<i32>` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0005`. diff --git a/tests/ui/lexer/dont-ice-on-invalid-lifetime-in-macro-definition.rs b/tests/ui/lexer/dont-ice-on-invalid-lifetime-in-macro-definition.rs new file mode 100644 index 00000000000..caae48dfd3b --- /dev/null +++ b/tests/ui/lexer/dont-ice-on-invalid-lifetime-in-macro-definition.rs @@ -0,0 +1,9 @@ +//@ edition:2021 +macro_rules! a { + ( ) => { + impl<'b> c for d { + e::<f'g> //~ ERROR prefix `f` is unknown + } + }; +} +fn main() {} diff --git a/tests/ui/lexer/dont-ice-on-invalid-lifetime-in-macro-definition.stderr b/tests/ui/lexer/dont-ice-on-invalid-lifetime-in-macro-definition.stderr new file mode 100644 index 00000000000..ecce2c66504 --- /dev/null +++ b/tests/ui/lexer/dont-ice-on-invalid-lifetime-in-macro-definition.stderr @@ -0,0 +1,14 @@ +error: prefix `f` is unknown + --> $DIR/dont-ice-on-invalid-lifetime-in-macro-definition.rs:5:17 + | +LL | e::<f'g> + | ^ unknown prefix + | + = note: prefixed identifiers and literals are reserved since Rust 2021 +help: consider inserting whitespace here + | +LL | e::<f 'g> + | + + +error: aborting due to 1 previous error + diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-3.rs b/tests/ui/lexer/lex-bad-str-literal-as-char-3.rs index 0ae227da5f1..52781d9c6d8 100644 --- a/tests/ui/lexer/lex-bad-str-literal-as-char-3.rs +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-3.rs @@ -3,5 +3,6 @@ //@[rust2021] edition:2021 fn main() { println!('hello world'); - //[rust2015,rust2018,rust2021]~^ ERROR unterminated character literal + //~^ ERROR unterminated character literal + //[rust2021]~| ERROR prefix `world` is unknown } diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2021.stderr b/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2021.stderr index 06f12742667..4170560cfcb 100644 --- a/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2021.stderr +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2021.stderr @@ -1,3 +1,15 @@ +error: prefix `world` is unknown + --> $DIR/lex-bad-str-literal-as-char-3.rs:5:21 + | +LL | println!('hello world'); + | ^^^^^ unknown prefix + | + = note: prefixed identifiers and literals are reserved since Rust 2021 +help: if you meant to write a string literal, use double quotes + | +LL | println!("hello world"); + | ~ ~ + error[E0762]: unterminated character literal --> $DIR/lex-bad-str-literal-as-char-3.rs:5:26 | @@ -9,6 +21,6 @@ help: if you meant to write a string literal, use double quotes LL | println!("hello world"); | ~ ~ -error: aborting due to 1 previous error +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0762`. diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-4.rs b/tests/ui/lexer/lex-bad-str-literal-as-char-4.rs new file mode 100644 index 00000000000..f0c7ad8f82b --- /dev/null +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-4.rs @@ -0,0 +1,9 @@ +//@edition:2021 +macro_rules! foo { + () => { + println!('hello world'); + //~^ ERROR unterminated character literal + //~| ERROR prefix `world` is unknown + } +} +fn main() {} diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-4.stderr b/tests/ui/lexer/lex-bad-str-literal-as-char-4.stderr new file mode 100644 index 00000000000..af42b5b7f7b --- /dev/null +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-4.stderr @@ -0,0 +1,26 @@ +error: prefix `world` is unknown + --> $DIR/lex-bad-str-literal-as-char-4.rs:4:25 + | +LL | println!('hello world'); + | ^^^^^ unknown prefix + | + = note: prefixed identifiers and literals are reserved since Rust 2021 +help: if you meant to write a string literal, use double quotes + | +LL | println!("hello world"); + | ~ ~ + +error[E0762]: unterminated character literal + --> $DIR/lex-bad-str-literal-as-char-4.rs:4:30 + | +LL | println!('hello world'); + | ^^^ + | +help: if you meant to write a string literal, use double quotes + | +LL | println!("hello world"); + | ~ ~ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0762`. diff --git a/tests/ui/linkage-attr/uikit-framework.rs b/tests/ui/linkage-attr/uikit-framework.rs new file mode 100644 index 00000000000..fca0332384a --- /dev/null +++ b/tests/ui/linkage-attr/uikit-framework.rs @@ -0,0 +1,25 @@ +//! Check that linking to UIKit on platforms where that is available works. +//@ revisions: ios tvos watchos visionos +//@ [ios]only-ios +//@ [tvos]only-tvos +//@ [watchos]only-watchos +//@ [visionos]only-visionos +//@ build-pass + +use std::ffi::{c_char, c_int, c_void}; + +#[link(name = "UIKit", kind = "framework")] +extern "C" { + pub fn UIApplicationMain( + argc: c_int, + argv: *const c_char, + principalClassName: *const c_void, + delegateClassName: *const c_void, + ) -> c_int; +} + +pub fn main() { + unsafe { + UIApplicationMain(0, core::ptr::null(), core::ptr::null(), core::ptr::null()); + } +} diff --git a/tests/ui/lint/unused/issue-59896.rs b/tests/ui/lint/unused/issue-59896.rs index ff9f19acf84..a98017524f5 100644 --- a/tests/ui/lint/unused/issue-59896.rs +++ b/tests/ui/lint/unused/issue-59896.rs @@ -1,9 +1,10 @@ +//@ check-pass #![deny(unused_imports)] struct S; fn main() { - use S; //~ ERROR the item `S` is imported redundantly + use S; //FIXME(unused_imports): ~ ERROR the item `S` is imported redundantly let _s = S; } diff --git a/tests/ui/lint/unused/issue-59896.stderr b/tests/ui/lint/unused/issue-59896.stderr deleted file mode 100644 index 3e8298c6b72..00000000000 --- a/tests/ui/lint/unused/issue-59896.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error: the item `S` is imported redundantly - --> $DIR/issue-59896.rs:6:9 - | -LL | struct S; - | --------- the item `S` is already defined here -... -LL | use S; - | ^ - | -note: the lint level is defined here - --> $DIR/issue-59896.rs:1:9 - | -LL | #![deny(unused_imports)] - | ^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/lint/unused/unused_parens/unused-parens-in-macro-issue-120642.rs b/tests/ui/lint/unused/unused_parens/unused-parens-in-macro-issue-120642.rs new file mode 100644 index 00000000000..59bb65a357b --- /dev/null +++ b/tests/ui/lint/unused/unused_parens/unused-parens-in-macro-issue-120642.rs @@ -0,0 +1,39 @@ +//@ run-pass + +#![warn(unused_parens)] +#![allow(dead_code)] + +trait Foo { + fn bar(); + fn tar(); +} + +macro_rules! unused_parens { + ($ty:ident) => { + impl<$ty: Foo> Foo for ($ty,) { + fn bar() { <$ty>::bar() } + fn tar() {} + } + }; + + ($ty:ident $(, $rest:ident)*) => { + impl<$ty: Foo, $($rest: Foo),*> Foo for ($ty, $($rest),*) { + fn bar() { + <$ty>::bar(); + <($($rest),*)>::bar() //~WARN unnecessary parentheses around type + } + fn tar() { + let (_t) = 1; //~WARN unnecessary parentheses around pattern + //~| WARN unnecessary parentheses around pattern + let (_t1,) = (1,); + let (_t2, _t3) = (1, 2); + } + } + + unused_parens!($($rest),*); + } +} + +unused_parens!(T1, T2, T3); + +fn main() {} diff --git a/tests/ui/lint/unused/unused_parens/unused-parens-in-macro-issue-120642.stderr b/tests/ui/lint/unused/unused_parens/unused-parens-in-macro-issue-120642.stderr new file mode 100644 index 00000000000..b1390debec7 --- /dev/null +++ b/tests/ui/lint/unused/unused_parens/unused-parens-in-macro-issue-120642.stderr @@ -0,0 +1,40 @@ +warning: unnecessary parentheses around pattern + --> $DIR/unused-parens-in-macro-issue-120642.rs:26:19 + | +LL | let (_t) = 1; + | ^^^^ +... +LL | unused_parens!(T1, T2, T3); + | -------------------------- in this macro invocation + | +note: the lint level is defined here + --> $DIR/unused-parens-in-macro-issue-120642.rs:3:9 + | +LL | #![warn(unused_parens)] + | ^^^^^^^^^^^^^ + = note: this warning originates in the macro `unused_parens` (in Nightly builds, run with -Z macro-backtrace for more info) + +warning: unnecessary parentheses around type + --> $DIR/unused-parens-in-macro-issue-120642.rs:23:18 + | +LL | <($($rest),*)>::bar() + | ^^^^^^^^^^^^ +... +LL | unused_parens!(T1, T2, T3); + | -------------------------- in this macro invocation + | + = note: this warning originates in the macro `unused_parens` (in Nightly builds, run with -Z macro-backtrace for more info) + +warning: unnecessary parentheses around pattern + --> $DIR/unused-parens-in-macro-issue-120642.rs:26:19 + | +LL | let (_t) = 1; + | ^^^^ +... +LL | unused_parens!(T1, T2, T3); + | -------------------------- in this macro invocation + | + = note: this warning originates in the macro `unused_parens` (in Nightly builds, run with -Z macro-backtrace for more info) + +warning: 3 warnings emitted + diff --git a/tests/ui/lint/use-redundant/use-redundant-glob-parent.rs b/tests/ui/lint/use-redundant/use-redundant-glob-parent.rs index 28d1fea98b5..797e57f48e9 100644 --- a/tests/ui/lint/use-redundant/use-redundant-glob-parent.rs +++ b/tests/ui/lint/use-redundant/use-redundant-glob-parent.rs @@ -9,7 +9,7 @@ pub mod bar { use bar::*; pub fn warning() -> Foo { - use bar::Foo; //~ WARNING imported redundantly + use bar::Foo; //FIXME(unused_imports): ~ WARNING imported redundantly Foo(Bar('a')) } diff --git a/tests/ui/lint/use-redundant/use-redundant-glob-parent.stderr b/tests/ui/lint/use-redundant/use-redundant-glob-parent.stderr deleted file mode 100644 index 2c3b3345270..00000000000 --- a/tests/ui/lint/use-redundant/use-redundant-glob-parent.stderr +++ /dev/null @@ -1,17 +0,0 @@ -warning: the item `Foo` is imported redundantly - --> $DIR/use-redundant-glob-parent.rs:12:9 - | -LL | use bar::*; - | ------ the item `Foo` is already imported here -... -LL | use bar::Foo; - | ^^^^^^^^ - | -note: the lint level is defined here - --> $DIR/use-redundant-glob-parent.rs:2:9 - | -LL | #![warn(unused_imports)] - | ^^^^^^^^^^^^^^ - -warning: 1 warning emitted - diff --git a/tests/ui/lint/use-redundant/use-redundant-glob.rs b/tests/ui/lint/use-redundant/use-redundant-glob.rs index 3d3fe2579b5..e5835be89d8 100644 --- a/tests/ui/lint/use-redundant/use-redundant-glob.rs +++ b/tests/ui/lint/use-redundant/use-redundant-glob.rs @@ -8,7 +8,7 @@ pub mod bar { pub fn warning() -> bar::Foo { use bar::*; - use bar::Foo; //~ WARNING imported redundantly + use bar::Foo; //FIXME(unused_imports): ~ WARNING imported redundantly Foo(Bar('a')) } diff --git a/tests/ui/lint/use-redundant/use-redundant-glob.stderr b/tests/ui/lint/use-redundant/use-redundant-glob.stderr deleted file mode 100644 index d3b406d82b6..00000000000 --- a/tests/ui/lint/use-redundant/use-redundant-glob.stderr +++ /dev/null @@ -1,16 +0,0 @@ -warning: the item `Foo` is imported redundantly - --> $DIR/use-redundant-glob.rs:11:9 - | -LL | use bar::*; - | ------ the item `Foo` is already imported here -LL | use bar::Foo; - | ^^^^^^^^ - | -note: the lint level is defined here - --> $DIR/use-redundant-glob.rs:2:9 - | -LL | #![warn(unused_imports)] - | ^^^^^^^^^^^^^^ - -warning: 1 warning emitted - diff --git a/tests/ui/lint/use-redundant/use-redundant-issue-71450.rs b/tests/ui/lint/use-redundant/use-redundant-issue-71450.rs index d0fb3454d3f..2db3435d46d 100644 --- a/tests/ui/lint/use-redundant/use-redundant-issue-71450.rs +++ b/tests/ui/lint/use-redundant/use-redundant-issue-71450.rs @@ -23,7 +23,8 @@ mod foo { fn main() { { - use std::string::String; //~ WARNING the item `String` is imported redundantly + use std::string::String; + //FIXME(unused_imports): ~^ WARNING the item `String` is imported redundantly // 'String' from 'std::string::String'. let s = String::new(); println!("{}", s); diff --git a/tests/ui/lint/use-redundant/use-redundant-issue-71450.stderr b/tests/ui/lint/use-redundant/use-redundant-issue-71450.stderr deleted file mode 100644 index b8832a31783..00000000000 --- a/tests/ui/lint/use-redundant/use-redundant-issue-71450.stderr +++ /dev/null @@ -1,17 +0,0 @@ -warning: the item `String` is imported redundantly - --> $DIR/use-redundant-issue-71450.rs:26:13 - | -LL | use std::string::String; - | ^^^^^^^^^^^^^^^^^^^ - --> $SRC_DIR/std/src/prelude/mod.rs:LL:COL - | - = note: the item `String` is already defined here - | -note: the lint level is defined here - --> $DIR/use-redundant-issue-71450.rs:3:9 - | -LL | #![warn(unused_imports)] - | ^^^^^^^^^^^^^^ - -warning: 1 warning emitted - diff --git a/tests/ui/lint/use-redundant/use-redundant-prelude-rust-2015.rs b/tests/ui/lint/use-redundant/use-redundant-prelude-rust-2015.rs index ae5118b2729..62f50c8a0df 100644 --- a/tests/ui/lint/use-redundant/use-redundant-prelude-rust-2015.rs +++ b/tests/ui/lint/use-redundant/use-redundant-prelude-rust-2015.rs @@ -2,11 +2,15 @@ #![warn(unused_imports)] -use std::option::Option::Some;//~ WARNING the item `Some` is imported redundantly -use std::option::Option::None; //~ WARNING the item `None` is imported redundantly +use std::option::Option::Some; +//FIXME(unused_imports): ~^ WARNING the item `Some` is imported redundantly +use std::option::Option::None; +//FIXME(unused_imports): ~ WARNING the item `None` is imported redundantly -use std::result::Result::Ok;//~ WARNING the item `Ok` is imported redundantly -use std::result::Result::Err;//~ WARNING the item `Err` is imported redundantly +use std::result::Result::Ok; +//FIXME(unused_imports): ~^ WARNING the item `Ok` is imported redundantly +use std::result::Result::Err; +//FIXME(unused_imports): ~^ WARNING the item `Err` is imported redundantly use std::convert::{TryFrom, TryInto}; fn main() { diff --git a/tests/ui/lint/use-redundant/use-redundant-prelude-rust-2015.stderr b/tests/ui/lint/use-redundant/use-redundant-prelude-rust-2015.stderr deleted file mode 100644 index 1b09df911eb..00000000000 --- a/tests/ui/lint/use-redundant/use-redundant-prelude-rust-2015.stderr +++ /dev/null @@ -1,44 +0,0 @@ -warning: the item `Some` is imported redundantly - --> $DIR/use-redundant-prelude-rust-2015.rs:5:5 - | -LL | use std::option::Option::Some; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - --> $SRC_DIR/std/src/prelude/mod.rs:LL:COL - | - = note: the item `Some` is already defined here - | -note: the lint level is defined here - --> $DIR/use-redundant-prelude-rust-2015.rs:2:9 - | -LL | #![warn(unused_imports)] - | ^^^^^^^^^^^^^^ - -warning: the item `None` is imported redundantly - --> $DIR/use-redundant-prelude-rust-2015.rs:6:5 - | -LL | use std::option::Option::None; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - --> $SRC_DIR/std/src/prelude/mod.rs:LL:COL - | - = note: the item `None` is already defined here - -warning: the item `Ok` is imported redundantly - --> $DIR/use-redundant-prelude-rust-2015.rs:8:5 - | -LL | use std::result::Result::Ok; - | ^^^^^^^^^^^^^^^^^^^^^^^ - --> $SRC_DIR/std/src/prelude/mod.rs:LL:COL - | - = note: the item `Ok` is already defined here - -warning: the item `Err` is imported redundantly - --> $DIR/use-redundant-prelude-rust-2015.rs:9:5 - | -LL | use std::result::Result::Err; - | ^^^^^^^^^^^^^^^^^^^^^^^^ - --> $SRC_DIR/std/src/prelude/mod.rs:LL:COL - | - = note: the item `Err` is already defined here - -warning: 4 warnings emitted - diff --git a/tests/ui/lint/use-redundant/use-redundant-prelude-rust-2021.rs b/tests/ui/lint/use-redundant/use-redundant-prelude-rust-2021.rs index cb4dcb6c0bd..1baa1ac1b8c 100644 --- a/tests/ui/lint/use-redundant/use-redundant-prelude-rust-2021.rs +++ b/tests/ui/lint/use-redundant/use-redundant-prelude-rust-2021.rs @@ -2,8 +2,10 @@ //@ edition:2021 #![warn(unused_imports)] -use std::convert::TryFrom;//~ WARNING the item `TryFrom` is imported redundantly -use std::convert::TryInto;//~ WARNING the item `TryInto` is imported redundantly +use std::convert::TryFrom; +//FIXME(unused_imports): ~^ WARNING the item `TryFrom` is imported redundantly +use std::convert::TryInto; +//FIXME(unused_imports): ~^ WARNING the item `TryInto` is imported redundantly fn main() { let _e: Result<i32, _> = 8u8.try_into(); diff --git a/tests/ui/lint/use-redundant/use-redundant-prelude-rust-2021.stderr b/tests/ui/lint/use-redundant/use-redundant-prelude-rust-2021.stderr deleted file mode 100644 index 542356dc996..00000000000 --- a/tests/ui/lint/use-redundant/use-redundant-prelude-rust-2021.stderr +++ /dev/null @@ -1,26 +0,0 @@ -warning: the item `TryFrom` is imported redundantly - --> $DIR/use-redundant-prelude-rust-2021.rs:5:5 - | -LL | use std::convert::TryFrom; - | ^^^^^^^^^^^^^^^^^^^^^ - --> $SRC_DIR/std/src/prelude/mod.rs:LL:COL - | - = note: the item `TryFrom` is already defined here - | -note: the lint level is defined here - --> $DIR/use-redundant-prelude-rust-2021.rs:3:9 - | -LL | #![warn(unused_imports)] - | ^^^^^^^^^^^^^^ - -warning: the item `TryInto` is imported redundantly - --> $DIR/use-redundant-prelude-rust-2021.rs:6:5 - | -LL | use std::convert::TryInto; - | ^^^^^^^^^^^^^^^^^^^^^ - --> $SRC_DIR/std/src/prelude/mod.rs:LL:COL - | - = note: the item `TryInto` is already defined here - -warning: 2 warnings emitted - diff --git a/tests/ui/lint/use-redundant/use-redundant.rs b/tests/ui/lint/use-redundant/use-redundant.rs index 88d3ee75a3f..9e4902af34b 100644 --- a/tests/ui/lint/use-redundant/use-redundant.rs +++ b/tests/ui/lint/use-redundant/use-redundant.rs @@ -18,7 +18,7 @@ use m1::*; //~ WARNING unused import use m2::*; //~ WARNING unused import fn main() { - use crate::foo::Bar; //~ WARNING imported redundantly + use crate::foo::Bar; //FIXME(unused_imports): ~ WARNING imported redundantly let _a: Bar = 3; baz(); diff --git a/tests/ui/lint/use-redundant/use-redundant.stderr b/tests/ui/lint/use-redundant/use-redundant.stderr index c861a1956e1..224e8411237 100644 --- a/tests/ui/lint/use-redundant/use-redundant.stderr +++ b/tests/ui/lint/use-redundant/use-redundant.stderr @@ -16,14 +16,5 @@ warning: unused import: `m2::*` LL | use m2::*; | ^^^^^ -warning: the item `Bar` is imported redundantly - --> $DIR/use-redundant.rs:21:9 - | -LL | use crate::foo::Bar; - | --------------- the item `Bar` is already imported here -... -LL | use crate::foo::Bar; - | ^^^^^^^^^^^^^^^ - -warning: 3 warnings emitted +warning: 2 warnings emitted diff --git a/tests/ui/loops/loop-break-value.rs b/tests/ui/loops/loop-break-value.rs index c35200520cb..d509fc16570 100644 --- a/tests/ui/loops/loop-break-value.rs +++ b/tests/ui/loops/loop-break-value.rs @@ -23,6 +23,10 @@ fn main() { }; }; + let _: Option<String> = loop { + break; //~ ERROR mismatched types + }; + 'while_loop: while true { //~ WARN denote infinite loops with break; break (); //~ ERROR `break` with value from a `while` loop diff --git a/tests/ui/loops/loop-break-value.stderr b/tests/ui/loops/loop-break-value.stderr index a691960f962..0093182422e 100644 --- a/tests/ui/loops/loop-break-value.stderr +++ b/tests/ui/loops/loop-break-value.stderr @@ -1,5 +1,5 @@ warning: label name `'a` shadows a label name that is already in scope - --> $DIR/loop-break-value.rs:136:17 + --> $DIR/loop-break-value.rs:140:17 | LL | 'a: loop { | -- first declared here @@ -8,7 +8,7 @@ LL | let _ = 'a: loop { | ^^ label `'a` already in scope warning: label name `'a` shadows a label name that is already in scope - --> $DIR/loop-break-value.rs:148:17 + --> $DIR/loop-break-value.rs:152:17 | LL | 'a: loop { | -- first declared here @@ -17,7 +17,7 @@ LL | let _ = 'a: loop { | ^^ label `'a` already in scope error[E0425]: cannot find value `LOOP` in this scope - --> $DIR/loop-break-value.rs:95:15 + --> $DIR/loop-break-value.rs:99:15 | LL | 'LOOP: for _ in 0 .. 9 { | ----- a label with a similar name exists @@ -28,7 +28,7 @@ LL | break LOOP; | help: use the similarly named label: `'LOOP` warning: denote infinite loops with `loop { ... }` - --> $DIR/loop-break-value.rs:26:5 + --> $DIR/loop-break-value.rs:30:5 | LL | 'while_loop: while true { | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `loop` @@ -36,7 +36,7 @@ LL | 'while_loop: while true { = note: `#[warn(while_true)]` on by default error[E0571]: `break` with value from a `while` loop - --> $DIR/loop-break-value.rs:28:9 + --> $DIR/loop-break-value.rs:32:9 | LL | 'while_loop: while true { | ----------------------- you can't `break` with a value in a `while` loop @@ -54,7 +54,7 @@ LL | break 'while_loop; | ~~~~~~~~~~~ error[E0571]: `break` with value from a `while` loop - --> $DIR/loop-break-value.rs:30:13 + --> $DIR/loop-break-value.rs:34:13 | LL | 'while_loop: while true { | ----------------------- you can't `break` with a value in a `while` loop @@ -68,7 +68,7 @@ LL | break 'while_loop; | ~~~~~~~~~~~~~~~~~ error[E0571]: `break` with value from a `while` loop - --> $DIR/loop-break-value.rs:38:12 + --> $DIR/loop-break-value.rs:42:12 | LL | while let Some(_) = Some(()) { | ---------------------------- you can't `break` with a value in a `while` loop @@ -81,7 +81,7 @@ LL | if break { | ~~~~~ error[E0571]: `break` with value from a `while` loop - --> $DIR/loop-break-value.rs:43:9 + --> $DIR/loop-break-value.rs:47:9 | LL | while let Some(_) = Some(()) { | ---------------------------- you can't `break` with a value in a `while` loop @@ -94,7 +94,7 @@ LL | break; | ~~~~~ error[E0571]: `break` with value from a `while` loop - --> $DIR/loop-break-value.rs:49:13 + --> $DIR/loop-break-value.rs:53:13 | LL | 'while_let_loop: while let Some(_) = Some(()) { | --------------------------------------------- you can't `break` with a value in a `while` loop @@ -108,7 +108,7 @@ LL | break 'while_let_loop; | ~~~~~~~~~~~~~~~~~~~~~ error[E0571]: `break` with value from a `for` loop - --> $DIR/loop-break-value.rs:56:9 + --> $DIR/loop-break-value.rs:60:9 | LL | for _ in &[1,2,3] { | ----------------- you can't `break` with a value in a `for` loop @@ -121,7 +121,7 @@ LL | break; | ~~~~~ error[E0571]: `break` with value from a `for` loop - --> $DIR/loop-break-value.rs:57:9 + --> $DIR/loop-break-value.rs:61:9 | LL | for _ in &[1,2,3] { | ----------------- you can't `break` with a value in a `for` loop @@ -135,7 +135,7 @@ LL | break; | ~~~~~ error[E0571]: `break` with value from a `for` loop - --> $DIR/loop-break-value.rs:64:13 + --> $DIR/loop-break-value.rs:68:13 | LL | 'for_loop: for _ in &[1,2,3] { | ---------------------------- you can't `break` with a value in a `for` loop @@ -191,7 +191,24 @@ LL | break 'outer_loop "nope"; | ^^^^^^ expected `i32`, found `&str` error[E0308]: mismatched types - --> $DIR/loop-break-value.rs:73:26 + --> $DIR/loop-break-value.rs:27:9 + | +LL | let _: Option<String> = loop { + | - ---- this loop is expected to be of type `Option<String>` + | | + | expected because of this assignment +LL | break; + | ^^^^^ expected `Option<String>`, found `()` + | + = note: expected enum `Option<String>` + found unit type `()` +help: give the `break` a value of the expected type + | +LL | break None; + | ++++ + +error[E0308]: mismatched types + --> $DIR/loop-break-value.rs:77:26 | LL | break; | ----- expected because of this `break` @@ -199,7 +216,7 @@ LL | break 'c 123; | ^^^ expected `()`, found integer error[E0308]: mismatched types - --> $DIR/loop-break-value.rs:80:15 + --> $DIR/loop-break-value.rs:84:15 | LL | break (break, break); | ^-----^^-----^ @@ -212,7 +229,7 @@ LL | break (break, break); found tuple `(!, !)` error[E0308]: mismatched types - --> $DIR/loop-break-value.rs:85:15 + --> $DIR/loop-break-value.rs:89:15 | LL | break; | ----- expected because of this `break` @@ -220,20 +237,20 @@ LL | break 2; | ^ expected `()`, found integer error[E0308]: mismatched types - --> $DIR/loop-break-value.rs:90:9 + --> $DIR/loop-break-value.rs:94:9 | LL | break 2; | ------- expected because of this `break` LL | break; | ^^^^^ expected integer, found `()` | -help: give it a value of the expected type +help: give the `break` a value of the expected type | LL | break value; | +++++ error[E0308]: mismatched types - --> $DIR/loop-break-value.rs:108:9 + --> $DIR/loop-break-value.rs:112:9 | LL | break 'a 1; | ---------- expected because of this `break` @@ -241,13 +258,13 @@ LL | break 'a 1; LL | break; | ^^^^^ expected integer, found `()` | -help: give it a value of the expected type +help: give the `break` a value of the expected type | LL | break value; | +++++ error[E0308]: mismatched types - --> $DIR/loop-break-value.rs:120:9 + --> $DIR/loop-break-value.rs:124:9 | LL | break 'a 1; | ---------- expected because of this `break` @@ -255,13 +272,13 @@ LL | break 'a 1; LL | break 'a; | ^^^^^^^^ expected integer, found `()` | -help: give it a value of the expected type +help: give the `break` a value of the expected type | LL | break 'a value; | +++++ error[E0308]: mismatched types - --> $DIR/loop-break-value.rs:131:15 + --> $DIR/loop-break-value.rs:135:15 | LL | break; | ----- expected because of this `break` @@ -270,7 +287,7 @@ LL | break 2; | ^ expected `()`, found integer error[E0308]: mismatched types - --> $DIR/loop-break-value.rs:140:17 + --> $DIR/loop-break-value.rs:144:17 | LL | break 2; | ------- expected because of this `break` @@ -278,13 +295,13 @@ LL | loop { LL | break 'a; | ^^^^^^^^ expected integer, found `()` | -help: give it a value of the expected type +help: give the `break` a value of the expected type | LL | break 'a value; | +++++ error[E0308]: mismatched types - --> $DIR/loop-break-value.rs:143:15 + --> $DIR/loop-break-value.rs:147:15 | LL | break; | ----- expected because of this `break` @@ -293,7 +310,7 @@ LL | break 2; | ^ expected `()`, found integer error[E0308]: mismatched types - --> $DIR/loop-break-value.rs:152:17 + --> $DIR/loop-break-value.rs:156:17 | LL | break 'a 2; | ---------- expected because of this `break` @@ -301,13 +318,13 @@ LL | loop { LL | break 'a; | ^^^^^^^^ expected integer, found `()` | -help: give it a value of the expected type +help: give the `break` a value of the expected type | LL | break 'a value; | +++++ error[E0308]: mismatched types - --> $DIR/loop-break-value.rs:155:15 + --> $DIR/loop-break-value.rs:159:15 | LL | break; | ----- expected because of this `break` @@ -316,7 +333,7 @@ LL | break 2; | ^ expected `()`, found integer error[E0308]: mismatched types - --> $DIR/loop-break-value.rs:159:15 + --> $DIR/loop-break-value.rs:163:15 | LL | fn main() { | - expected `()` because of this return type @@ -326,7 +343,7 @@ LL | loop { // point at the return type LL | break 2; | ^ expected `()`, found integer -error: aborting due to 25 previous errors; 3 warnings emitted +error: aborting due to 26 previous errors; 3 warnings emitted Some errors have detailed explanations: E0308, E0425, E0571. For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/loops/loop-labeled-break-value.stderr b/tests/ui/loops/loop-labeled-break-value.stderr index 694d6c306f6..3be62316e34 100644 --- a/tests/ui/loops/loop-labeled-break-value.stderr +++ b/tests/ui/loops/loop-labeled-break-value.stderr @@ -7,7 +7,7 @@ LL | let _: i32 = loop { break }; | | this loop is expected to be of type `i32` | expected because of this assignment | -help: give it a value of the expected type +help: give the `break` a value of the expected type | LL | let _: i32 = loop { break 42 }; | ++ @@ -21,7 +21,7 @@ LL | let _: i32 = 'inner: loop { break 'inner }; | | this loop is expected to be of type `i32` | expected because of this assignment | -help: give it a value of the expected type +help: give the `break` a value of the expected type | LL | let _: i32 = 'inner: loop { break 'inner 42 }; | ++ @@ -35,7 +35,7 @@ LL | let _: i32 = 'inner2: loop { loop { break 'inner2 } }; | | this loop is expected to be of type `i32` | expected because of this assignment | -help: give it a value of the expected type +help: give the `break` a value of the expected type | LL | let _: i32 = 'inner2: loop { loop { break 'inner2 42 } }; | ++ diff --git a/tests/ui/loops/loop-proper-liveness.stderr b/tests/ui/loops/loop-proper-liveness.stderr index bcd6eb353e5..cd4c064bcd1 100644 --- a/tests/ui/loops/loop-proper-liveness.stderr +++ b/tests/ui/loops/loop-proper-liveness.stderr @@ -10,8 +10,8 @@ LL | println!("{:?}", x); = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider assigning a value | -LL | let x: i32 = 0; - | +++ +LL | let x: i32 = 42; + | ++++ error: aborting due to 1 previous error diff --git a/tests/ui/loops/loop-properly-diverging-2.stderr b/tests/ui/loops/loop-properly-diverging-2.stderr index c9f27a6a672..ba615f9ae4f 100644 --- a/tests/ui/loops/loop-properly-diverging-2.stderr +++ b/tests/ui/loops/loop-properly-diverging-2.stderr @@ -7,7 +7,7 @@ LL | let x: i32 = loop { break }; | | this loop is expected to be of type `i32` | expected because of this assignment | -help: give it a value of the expected type +help: give the `break` a value of the expected type | LL | let x: i32 = loop { break 42 }; | ++ diff --git a/tests/ui/methods/method-ambig-one-trait-unknown-int-type.stderr b/tests/ui/methods/method-ambig-one-trait-unknown-int-type.stderr index 0a022dc3984..b2d2d039ff6 100644 --- a/tests/ui/methods/method-ambig-one-trait-unknown-int-type.stderr +++ b/tests/ui/methods/method-ambig-one-trait-unknown-int-type.stderr @@ -1,4 +1,4 @@ -error[E0282]: type annotations needed for `Vec<T>` +error[E0282]: type annotations needed for `Vec<_>` --> $DIR/method-ambig-one-trait-unknown-int-type.rs:24:9 | LL | let mut x = Vec::new(); diff --git a/tests/ui/methods/suggest-convert-ptr-to-ref.rs b/tests/ui/methods/suggest-convert-ptr-to-ref.rs new file mode 100644 index 00000000000..ccce3c65470 --- /dev/null +++ b/tests/ui/methods/suggest-convert-ptr-to-ref.rs @@ -0,0 +1,17 @@ +fn main() { + let mut x = 8u8; + let z: *const u8 = &x; + // issue #21596 + println!("{}", z.to_string()); //~ ERROR E0599 + + let t: *mut u8 = &mut x; + println!("{}", t.to_string()); //~ ERROR E0599 + t.make_ascii_lowercase(); //~ ERROR E0599 + + // suggest `as_mut` simply because the name is similar + let _ = t.as_mut_ref(); //~ ERROR E0599 + let _ = t.as_ref_mut(); //~ ERROR E0599 + + // no ptr-to-ref suggestion + z.make_ascii_lowercase(); //~ ERROR E0599 +} diff --git a/tests/ui/methods/suggest-convert-ptr-to-ref.stderr b/tests/ui/methods/suggest-convert-ptr-to-ref.stderr new file mode 100644 index 00000000000..69b20d57be8 --- /dev/null +++ b/tests/ui/methods/suggest-convert-ptr-to-ref.stderr @@ -0,0 +1,70 @@ +error[E0599]: `*const u8` doesn't implement `std::fmt::Display` + --> $DIR/suggest-convert-ptr-to-ref.rs:5:22 + | +LL | println!("{}", z.to_string()); + | ^^^^^^^^^ `*const u8` cannot be formatted with the default formatter + | +note: the method `to_string` exists on the type `&u8` + --> $SRC_DIR/alloc/src/string.rs:LL:COL + = note: you might want to use the unsafe method `<*const T>::as_ref` to get an optional reference to the value behind the pointer + = note: read the documentation for `<*const T>::as_ref` and ensure you satisfy its safety preconditions before calling it to avoid undefined behavior: https://doc.rust-lang.org/std/primitive.pointer.html#method.as_ref + = note: the following trait bounds were not satisfied: + `*const u8: std::fmt::Display` + which is required by `*const u8: ToString` + +error[E0599]: `*mut u8` doesn't implement `std::fmt::Display` + --> $DIR/suggest-convert-ptr-to-ref.rs:8:22 + | +LL | println!("{}", t.to_string()); + | ^^^^^^^^^ `*mut u8` cannot be formatted with the default formatter + | +note: the method `to_string` exists on the type `&&mut u8` + --> $SRC_DIR/alloc/src/string.rs:LL:COL + = note: you might want to use the unsafe method `<*mut T>::as_ref` to get an optional reference to the value behind the pointer + = note: read the documentation for `<*mut T>::as_ref` and ensure you satisfy its safety preconditions before calling it to avoid undefined behavior: https://doc.rust-lang.org/std/primitive.pointer.html#method.as_ref-1 + = note: the following trait bounds were not satisfied: + `*mut u8: std::fmt::Display` + which is required by `*mut u8: ToString` + +error[E0599]: no method named `make_ascii_lowercase` found for raw pointer `*mut u8` in the current scope + --> $DIR/suggest-convert-ptr-to-ref.rs:9:7 + | +LL | t.make_ascii_lowercase(); + | ^^^^^^^^^^^^^^^^^^^^ method not found in `*mut u8` + | +note: the method `make_ascii_lowercase` exists on the type `&mut u8` + --> $SRC_DIR/core/src/num/mod.rs:LL:COL + = note: you might want to use the unsafe method `<*mut T>::as_mut` to get an optional reference to the value behind the pointer + = note: read the documentation for `<*mut T>::as_mut` and ensure you satisfy its safety preconditions before calling it to avoid undefined behavior: https://doc.rust-lang.org/std/primitive.pointer.html#method.as_mut + +error[E0599]: no method named `as_mut_ref` found for raw pointer `*mut u8` in the current scope + --> $DIR/suggest-convert-ptr-to-ref.rs:12:15 + | +LL | let _ = t.as_mut_ref(); + | ^^^^^^^^^^ + | +help: there is a method `as_mut` with a similar name + | +LL | let _ = t.as_mut(); + | ~~~~~~ + +error[E0599]: no method named `as_ref_mut` found for raw pointer `*mut u8` in the current scope + --> $DIR/suggest-convert-ptr-to-ref.rs:13:15 + | +LL | let _ = t.as_ref_mut(); + | ^^^^^^^^^^ + | +help: there is a method `as_mut` with a similar name + | +LL | let _ = t.as_mut(); + | ~~~~~~ + +error[E0599]: no method named `make_ascii_lowercase` found for raw pointer `*const u8` in the current scope + --> $DIR/suggest-convert-ptr-to-ref.rs:16:7 + | +LL | z.make_ascii_lowercase(); + | ^^^^^^^^^^^^^^^^^^^^ method not found in `*const u8` + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/mir/issue-102389.stderr b/tests/ui/mir/issue-102389.stderr index 1f04d119b56..838eaffb5a0 100644 --- a/tests/ui/mir/issue-102389.stderr +++ b/tests/ui/mir/issue-102389.stderr @@ -3,6 +3,12 @@ error[E0507]: cannot move out of `*inbounds` which is behind a shared reference | LL | array[*inbounds as usize] | ^^^^^^^^^ move occurs because `*inbounds` has type `Enum`, which does not implement the `Copy` trait + | +note: if `Enum` implemented `Clone`, you could clone the value + --> $DIR/issue-102389.rs:1:1 + | +LL | enum Enum { A, B, C } + | ^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/mismatched_types/binops.stderr b/tests/ui/mismatched_types/binops.stderr index f8047c8e2d4..099c580a056 100644 --- a/tests/ui/mismatched_types/binops.stderr +++ b/tests/ui/mismatched_types/binops.stderr @@ -6,15 +6,15 @@ LL | 1 + Some(1); | = help: the trait `Add<Option<{integer}>>` is not implemented for `{integer}` = help: the following other types implement trait `Add<Rhs>`: + <&'a f128 as Add<f128>> + <&'a f16 as Add<f16>> <&'a f32 as Add<f32>> <&'a f64 as Add<f64>> <&'a i128 as Add<i128>> <&'a i16 as Add<i16>> <&'a i32 as Add<i32>> <&'a i64 as Add<i64>> - <&'a i8 as Add<i8>> - <&'a isize as Add<isize>> - and 48 others + and 56 others error[E0277]: cannot subtract `Option<{integer}>` from `usize` --> $DIR/binops.rs:3:16 @@ -37,15 +37,15 @@ LL | 3 * (); | = help: the trait `Mul<()>` is not implemented for `{integer}` = help: the following other types implement trait `Mul<Rhs>`: + <&'a f128 as Mul<f128>> + <&'a f16 as Mul<f16>> <&'a f32 as Mul<f32>> <&'a f64 as Mul<f64>> <&'a i128 as Mul<i128>> <&'a i16 as Mul<i16>> <&'a i32 as Mul<i32>> <&'a i64 as Mul<i64>> - <&'a i8 as Mul<i8>> - <&'a isize as Mul<isize>> - and 49 others + and 57 others error[E0277]: cannot divide `{integer}` by `&str` --> $DIR/binops.rs:5:7 @@ -55,15 +55,15 @@ LL | 4 / ""; | = help: the trait `Div<&str>` is not implemented for `{integer}` = help: the following other types implement trait `Div<Rhs>`: + <&'a f128 as Div<f128>> + <&'a f16 as Div<f16>> <&'a f32 as Div<f32>> <&'a f64 as Div<f64>> <&'a i128 as Div<i128>> <&'a i16 as Div<i16>> <&'a i32 as Div<i32>> <&'a i64 as Div<i64>> - <&'a i8 as Div<i8>> - <&'a isize as Div<isize>> - and 54 others + and 62 others error[E0277]: can't compare `{integer}` with `String` --> $DIR/binops.rs:6:7 diff --git a/tests/ui/moves/issue-72649-uninit-in-loop.rs b/tests/ui/moves/issue-72649-uninit-in-loop.rs index 56c225bab8c..86f389cb3af 100644 --- a/tests/ui/moves/issue-72649-uninit-in-loop.rs +++ b/tests/ui/moves/issue-72649-uninit-in-loop.rs @@ -3,6 +3,10 @@ // 'value moved in previous iteration of loop' message struct NonCopy; +//~^ NOTE if `NonCopy` implemented `Clone` +//~| NOTE if `NonCopy` implemented `Clone` +//~| NOTE if `NonCopy` implemented `Clone` +//~| NOTE if `NonCopy` implemented `Clone` fn good() { loop { diff --git a/tests/ui/moves/issue-72649-uninit-in-loop.stderr b/tests/ui/moves/issue-72649-uninit-in-loop.stderr index 7e119fe8cda..35216f8a66f 100644 --- a/tests/ui/moves/issue-72649-uninit-in-loop.stderr +++ b/tests/ui/moves/issue-72649-uninit-in-loop.stderr @@ -1,5 +1,5 @@ error[E0382]: use of moved value: `value` - --> $DIR/issue-72649-uninit-in-loop.rs:20:22 + --> $DIR/issue-72649-uninit-in-loop.rs:24:22 | LL | let value = NonCopy{}; | ----- move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait @@ -9,9 +9,15 @@ LL | let _used = value; LL | LL | let _used2 = value; | ^^^^^ value used here after move + | +note: if `NonCopy` implemented `Clone`, you could clone the value + --> $DIR/issue-72649-uninit-in-loop.rs:5:1 + | +LL | struct NonCopy; + | ^^^^^^^^^^^^^^ error[E0382]: use of moved value: `value` - --> $DIR/issue-72649-uninit-in-loop.rs:32:26 + --> $DIR/issue-72649-uninit-in-loop.rs:36:26 | LL | let value = NonCopy{}; | ----- move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait @@ -23,9 +29,15 @@ LL | let _used = value; ... LL | let _used2 = value; | ^^^^^ value used here after move + | +note: if `NonCopy` implemented `Clone`, you could clone the value + --> $DIR/issue-72649-uninit-in-loop.rs:5:1 + | +LL | struct NonCopy; + | ^^^^^^^^^^^^^^ error[E0382]: use of moved value: `value` - --> $DIR/issue-72649-uninit-in-loop.rs:42:21 + --> $DIR/issue-72649-uninit-in-loop.rs:46:21 | LL | let value = NonCopy{}; | ----- move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait @@ -34,9 +46,15 @@ LL | loop { | ---- inside of this loop LL | let _used = value; | ^^^^^ value moved here, in previous iteration of loop + | +note: if `NonCopy` implemented `Clone`, you could clone the value + --> $DIR/issue-72649-uninit-in-loop.rs:5:1 + | +LL | struct NonCopy; + | ^^^^^^^^^^^^^^ error[E0382]: use of moved value: `value` - --> $DIR/issue-72649-uninit-in-loop.rs:53:22 + --> $DIR/issue-72649-uninit-in-loop.rs:57:22 | LL | let mut value = NonCopy{}; | --------- move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait @@ -45,9 +63,15 @@ LL | loop { | ---- inside of this loop LL | let _used2 = value; | ^^^^^ value moved here, in previous iteration of loop + | +note: if `NonCopy` implemented `Clone`, you could clone the value + --> $DIR/issue-72649-uninit-in-loop.rs:5:1 + | +LL | struct NonCopy; + | ^^^^^^^^^^^^^^ error[E0381]: used binding `value` isn't initialized - --> $DIR/issue-72649-uninit-in-loop.rs:61:21 + --> $DIR/issue-72649-uninit-in-loop.rs:65:21 | LL | let value: NonCopy; | ----- binding declared here but left uninitialized @@ -56,11 +80,11 @@ LL | let _used = value; | help: consider assigning a value | -LL | let value: NonCopy = todo!(); - | +++++++++ +LL | let value: NonCopy = value; + | +++++++ error[E0381]: used binding `value` isn't initialized - --> $DIR/issue-72649-uninit-in-loop.rs:69:21 + --> $DIR/issue-72649-uninit-in-loop.rs:73:21 | LL | let mut value: NonCopy; | --------- binding declared here but left uninitialized @@ -70,8 +94,8 @@ LL | let _used = value; | help: consider assigning a value | -LL | let mut value: NonCopy = todo!(); - | +++++++++ +LL | let mut value: NonCopy = value; + | +++++++ error: aborting due to 6 previous errors diff --git a/tests/ui/moves/issue-75904-move-closure-loop.stderr b/tests/ui/moves/issue-75904-move-closure-loop.stderr index 6f04105a35e..b6ad906bbdb 100644 --- a/tests/ui/moves/issue-75904-move-closure-loop.stderr +++ b/tests/ui/moves/issue-75904-move-closure-loop.stderr @@ -4,11 +4,18 @@ error[E0382]: use of moved value: `a` LL | let mut a = NotCopy; | ----- move occurs because `a` has type `NotCopy`, which does not implement the `Copy` trait LL | loop { + | ---- inside of this loop LL | || { | ^^ value moved into closure here, in previous iteration of loop LL | &mut a; LL | a; | - use occurs due to use in closure + | +note: if `NotCopy` implemented `Clone`, you could clone the value + --> $DIR/issue-75904-move-closure-loop.rs:5:1 + | +LL | struct NotCopy; + | ^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/moves/move-fn-self-receiver.stderr b/tests/ui/moves/move-fn-self-receiver.stderr index 17f48f5f7bf..e6bf52276ac 100644 --- a/tests/ui/moves/move-fn-self-receiver.stderr +++ b/tests/ui/moves/move-fn-self-receiver.stderr @@ -101,6 +101,12 @@ LL | mut_foo; | ^^^^^^^ move out of `mut_foo` occurs here LL | ret; | --- borrow later used here + | +note: if `Foo` implemented `Clone`, you could clone the value + --> $DIR/move-fn-self-receiver.rs:5:1 + | +LL | struct Foo; + | ^^^^^^^^^^ error[E0382]: use of moved value: `rc_foo` --> $DIR/move-fn-self-receiver.rs:55:5 @@ -132,6 +138,11 @@ LL | foo_add + Foo; LL | foo_add; | ^^^^^^^ value used here after move | +note: if `Foo` implemented `Clone`, you could clone the value + --> $DIR/move-fn-self-receiver.rs:5:1 + | +LL | struct Foo; + | ^^^^^^^^^^ note: calling this operator moves the left-hand side --> $SRC_DIR/core/src/ops/arith.rs:LL:COL diff --git a/tests/ui/moves/move-into-dead-array-1.stderr b/tests/ui/moves/move-into-dead-array-1.stderr index 83779fb16ed..d9b719730d6 100644 --- a/tests/ui/moves/move-into-dead-array-1.stderr +++ b/tests/ui/moves/move-into-dead-array-1.stderr @@ -8,8 +8,8 @@ LL | a[i] = d(); | help: consider assigning a value | -LL | let mut a: [D; 4] = todo!(); - | +++++++++ +LL | let mut a: [D; 4] = [value; 4]; + | ++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/moves/move-of-addr-of-mut.stderr b/tests/ui/moves/move-of-addr-of-mut.stderr index 706b52d3402..46f7d39a61a 100644 --- a/tests/ui/moves/move-of-addr-of-mut.stderr +++ b/tests/ui/moves/move-of-addr-of-mut.stderr @@ -9,8 +9,8 @@ LL | std::ptr::addr_of_mut!(x); = note: this error originates in the macro `std::ptr::addr_of_mut` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider assigning a value | -LL | let mut x: S = todo!(); - | +++++++++ +LL | let mut x: S = value; + | +++++++ error: aborting due to 1 previous error diff --git a/tests/ui/moves/move-out-of-array-1.stderr b/tests/ui/moves/move-out-of-array-1.stderr index aa0251dbd85..9e4a08e0cef 100644 --- a/tests/ui/moves/move-out-of-array-1.stderr +++ b/tests/ui/moves/move-out-of-array-1.stderr @@ -6,6 +6,12 @@ LL | a[i] | | | cannot move out of here | move occurs because `a[_]` has type `D`, which does not implement the `Copy` trait + | +note: if `D` implemented `Clone`, you could clone the value + --> $DIR/move-out-of-array-1.rs:5:1 + | +LL | struct D { _x: u8 } + | ^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/moves/moves-based-on-type-capture-clause-bad.rs b/tests/ui/moves/moves-based-on-type-capture-clause-bad.rs index b2f68352f89..9d7277c1c24 100644 --- a/tests/ui/moves/moves-based-on-type-capture-clause-bad.rs +++ b/tests/ui/moves/moves-based-on-type-capture-clause-bad.rs @@ -2,7 +2,7 @@ use std::thread; fn main() { let x = "Hello world!".to_string(); - thread::spawn(move|| { + thread::spawn(move || { println!("{}", x); }); println!("{}", x); //~ ERROR borrow of moved value diff --git a/tests/ui/moves/moves-based-on-type-capture-clause-bad.stderr b/tests/ui/moves/moves-based-on-type-capture-clause-bad.stderr index 5e527bf445e..c2b9aeab237 100644 --- a/tests/ui/moves/moves-based-on-type-capture-clause-bad.stderr +++ b/tests/ui/moves/moves-based-on-type-capture-clause-bad.stderr @@ -3,8 +3,8 @@ error[E0382]: borrow of moved value: `x` | LL | let x = "Hello world!".to_string(); | - move occurs because `x` has type `String`, which does not implement the `Copy` trait -LL | thread::spawn(move|| { - | ------ value moved into closure here +LL | thread::spawn(move || { + | ------- value moved into closure here LL | println!("{}", x); | - variable moved due to use in closure LL | }); diff --git a/tests/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr b/tests/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr index 513631b2060..523134a9425 100644 --- a/tests/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr +++ b/tests/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr @@ -7,6 +7,11 @@ LL | let _f = to_fn(|| test(i)); | -- ^ move occurs because `i` has type `Box<usize>`, which does not implement the `Copy` trait | | | captured by this `Fn` closure + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let _f = to_fn(|| test(i.clone())); + | ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/moves/needs-clone-through-deref.fixed b/tests/ui/moves/needs-clone-through-deref.fixed index 43ea15d1b63..8b201c4720d 100644 --- a/tests/ui/moves/needs-clone-through-deref.fixed +++ b/tests/ui/moves/needs-clone-through-deref.fixed @@ -12,7 +12,7 @@ impl Deref for S { impl S { fn foo(&self) { // `self.clone()` returns `&S`, not `Vec` - for _ in <Vec<usize> as Clone>::clone(&self.clone()).into_iter() {} //~ ERROR cannot move out of dereference of `S` + for _ in <Vec<usize> as Clone>::clone(&self).into_iter() {} //~ ERROR cannot move out of dereference of `S` } } fn main() {} diff --git a/tests/ui/moves/needs-clone-through-deref.stderr b/tests/ui/moves/needs-clone-through-deref.stderr index ff92f32e8d2..1f9aefeb4dd 100644 --- a/tests/ui/moves/needs-clone-through-deref.stderr +++ b/tests/ui/moves/needs-clone-through-deref.stderr @@ -10,8 +10,8 @@ note: `into_iter` takes ownership of the receiver `self`, which moves value --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL help: you can `clone` the value and consume it, but this might not be your desired behavior | -LL | for _ in <Vec<usize> as Clone>::clone(&self.clone()).into_iter() {} - | ++++++++++++++++++++++++++++++ + +LL | for _ in <Vec<usize> as Clone>::clone(&self).into_iter() {} + | ++++++++++++++++++++++++++++++ ~ error: aborting due to 1 previous error diff --git a/tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.fixed b/tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.fixed index b3eae0b22b6..cdc86f61d24 100644 --- a/tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.fixed +++ b/tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.fixed @@ -18,7 +18,7 @@ pub fn hashmap_copy<T, U>( map: &HashMap<T, U, Hash128_1>, ) where T: Hash + Clone, U: Clone { - let mut copy: Vec<U> = <HashMap<T, U, Hash128_1> as Clone>::clone(&map.clone()).into_values().collect(); //~ ERROR + let mut copy: Vec<U> = <HashMap<T, U, Hash128_1> as Clone>::clone(&map).into_values().collect(); //~ ERROR } pub fn make_map() -> HashMap<String, i64, Hash128_1> diff --git a/tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.stderr b/tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.stderr index 403daf8ff7c..755bbc5c21b 100644 --- a/tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.stderr +++ b/tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.stderr @@ -10,8 +10,8 @@ note: `HashMap::<K, V, S>::into_values` takes ownership of the receiver `self`, --> $SRC_DIR/std/src/collections/hash/map.rs:LL:COL help: you could `clone` the value and consume it, if the `Hash128_1: Clone` trait bound could be satisfied | -LL | let mut copy: Vec<U> = <HashMap<T, U, Hash128_1> as Clone>::clone(&map.clone()).into_values().collect(); - | ++++++++++++++++++++++++++++++++++++++++++++ + +LL | let mut copy: Vec<U> = <HashMap<T, U, Hash128_1> as Clone>::clone(&map).into_values().collect(); + | ++++++++++++++++++++++++++++++++++++++++++++ ~ help: consider annotating `Hash128_1` with `#[derive(Clone)]` | LL + #[derive(Clone)] diff --git a/tests/ui/moves/suggest-clone.fixed b/tests/ui/moves/suggest-clone.fixed index 59276a7b96d..59a162e72c1 100644 --- a/tests/ui/moves/suggest-clone.fixed +++ b/tests/ui/moves/suggest-clone.fixed @@ -7,5 +7,5 @@ impl Foo { } fn main() { let foo = &Foo; - <Foo as Clone>::clone(&foo).foo(); //~ ERROR cannot move out + <Foo as Clone>::clone(&foo.clone()).foo(); //~ ERROR cannot move out } diff --git a/tests/ui/moves/suggest-clone.stderr b/tests/ui/moves/suggest-clone.stderr index 25e89a58955..f8e0ccdfcef 100644 --- a/tests/ui/moves/suggest-clone.stderr +++ b/tests/ui/moves/suggest-clone.stderr @@ -15,6 +15,10 @@ help: you can `clone` the value and consume it, but this might not be your desir | LL | <Foo as Clone>::clone(&foo).foo(); | +++++++++++++++++++++++ + +help: consider cloning the value if the performance cost is acceptable + | +LL | foo.clone().foo(); + | ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/never_type/issue-52443.stderr b/tests/ui/never_type/issue-52443.stderr index bab47064f6c..02cb9cb22a3 100644 --- a/tests/ui/never_type/issue-52443.stderr +++ b/tests/ui/never_type/issue-52443.stderr @@ -36,7 +36,7 @@ error[E0308]: mismatched types LL | [(); loop { break }]; | ^^^^^ expected `usize`, found `()` | -help: give it a value of the expected type +help: give the `break` a value of the expected type | LL | [(); loop { break 42 }]; | ++ diff --git a/tests/ui/nll/cannot-move-block-spans.stderr b/tests/ui/nll/cannot-move-block-spans.stderr index 0dc5c08ea5f..d96773e1edf 100644 --- a/tests/ui/nll/cannot-move-block-spans.stderr +++ b/tests/ui/nll/cannot-move-block-spans.stderr @@ -9,6 +9,11 @@ help: consider removing the dereference here LL - let x = { *r }; LL + let x = { r }; | +help: consider cloning the value if the performance cost is acceptable + | +LL - let x = { *r }; +LL + let x = { r.clone() }; + | error[E0507]: cannot move out of `*r` which is behind a shared reference --> $DIR/cannot-move-block-spans.rs:6:22 @@ -21,6 +26,11 @@ help: consider removing the dereference here LL - let y = unsafe { *r }; LL + let y = unsafe { r }; | +help: consider cloning the value if the performance cost is acceptable + | +LL - let y = unsafe { *r }; +LL + let y = unsafe { r.clone() }; + | error[E0507]: cannot move out of `*r` which is behind a shared reference --> $DIR/cannot-move-block-spans.rs:7:26 @@ -33,6 +43,11 @@ help: consider removing the dereference here LL - let z = loop { break *r; }; LL + let z = loop { break r; }; | +help: consider cloning the value if the performance cost is acceptable + | +LL - let z = loop { break *r; }; +LL + let z = loop { break r.clone(); }; + | error[E0508]: cannot move out of type `[String; 2]`, a non-copy array --> $DIR/cannot-move-block-spans.rs:11:15 @@ -47,6 +62,10 @@ help: consider borrowing here | LL | let x = { &arr[0] }; | + +help: consider cloning the value if the performance cost is acceptable + | +LL | let x = { arr[0].clone() }; + | ++++++++ error[E0508]: cannot move out of type `[String; 2]`, a non-copy array --> $DIR/cannot-move-block-spans.rs:12:22 @@ -61,6 +80,10 @@ help: consider borrowing here | LL | let y = unsafe { &arr[0] }; | + +help: consider cloning the value if the performance cost is acceptable + | +LL | let y = unsafe { arr[0].clone() }; + | ++++++++ error[E0508]: cannot move out of type `[String; 2]`, a non-copy array --> $DIR/cannot-move-block-spans.rs:13:26 @@ -75,6 +98,10 @@ help: consider borrowing here | LL | let z = loop { break &arr[0]; }; | + +help: consider cloning the value if the performance cost is acceptable + | +LL | let z = loop { break arr[0].clone(); }; + | ++++++++ error[E0507]: cannot move out of `*r` which is behind a shared reference --> $DIR/cannot-move-block-spans.rs:17:38 @@ -87,6 +114,11 @@ help: consider removing the dereference here LL - let x = { let mut u = 0; u += 1; *r }; LL + let x = { let mut u = 0; u += 1; r }; | +help: consider cloning the value if the performance cost is acceptable + | +LL - let x = { let mut u = 0; u += 1; *r }; +LL + let x = { let mut u = 0; u += 1; r.clone() }; + | error[E0507]: cannot move out of `*r` which is behind a shared reference --> $DIR/cannot-move-block-spans.rs:18:45 @@ -99,6 +131,11 @@ help: consider removing the dereference here LL - let y = unsafe { let mut u = 0; u += 1; *r }; LL + let y = unsafe { let mut u = 0; u += 1; r }; | +help: consider cloning the value if the performance cost is acceptable + | +LL - let y = unsafe { let mut u = 0; u += 1; *r }; +LL + let y = unsafe { let mut u = 0; u += 1; r.clone() }; + | error[E0507]: cannot move out of `*r` which is behind a shared reference --> $DIR/cannot-move-block-spans.rs:19:49 @@ -111,6 +148,11 @@ help: consider removing the dereference here LL - let z = loop { let mut u = 0; u += 1; break *r; u += 2; }; LL + let z = loop { let mut u = 0; u += 1; break r; u += 2; }; | +help: consider cloning the value if the performance cost is acceptable + | +LL - let z = loop { let mut u = 0; u += 1; break *r; u += 2; }; +LL + let z = loop { let mut u = 0; u += 1; break r.clone(); u += 2; }; + | error: aborting due to 9 previous errors diff --git a/tests/ui/nll/closure-access-spans.stderr b/tests/ui/nll/closure-access-spans.stderr index 3e98fbd5e1d..f789e5e9f95 100644 --- a/tests/ui/nll/closure-access-spans.stderr +++ b/tests/ui/nll/closure-access-spans.stderr @@ -57,6 +57,12 @@ LL | || x; | move out of `x` occurs here LL | r.use_ref(); | - borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL - let r = &x; +LL + let r = x.clone(); + | error[E0382]: borrow of moved value: `x` --> $DIR/closure-access-spans.rs:35:5 @@ -103,6 +109,11 @@ LL | || *x = String::new(); | ^^ -- borrow occurs due to use in closure | | | value borrowed here after move + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let r = x.clone(); + | ++++++++ error[E0382]: use of moved value: `x` --> $DIR/closure-access-spans.rs:50:5 @@ -115,6 +126,11 @@ LL | || x; | ^^ - use occurs due to use in closure | | | value used here after move + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let r = x.clone(); + | ++++++++ error: aborting due to 9 previous errors diff --git a/tests/ui/nll/closure-move-spans.fixed b/tests/ui/nll/closure-move-spans.fixed new file mode 100644 index 00000000000..edd74e434e0 --- /dev/null +++ b/tests/ui/nll/closure-move-spans.fixed @@ -0,0 +1,23 @@ +// check that moves due to a closure capture give a special note +//@ run-rustfix +#![allow(unused_variables, unused_must_use, dead_code)] + +fn move_after_move(x: String) { + || x.clone(); + let y = x; //~ ERROR +} + +fn borrow_after_move(x: String) { + || x.clone(); + let y = &x; //~ ERROR +} + +fn borrow_mut_after_move(mut x: String) { + || x.clone(); + let y = &mut x; //~ ERROR +} + +fn fn_ref<F: Fn()>(f: F) -> F { f } +fn fn_mut<F: FnMut()>(f: F) -> F { f } + +fn main() {} diff --git a/tests/ui/nll/closure-move-spans.rs b/tests/ui/nll/closure-move-spans.rs index bf2431870a9..bba5c3776e6 100644 --- a/tests/ui/nll/closure-move-spans.rs +++ b/tests/ui/nll/closure-move-spans.rs @@ -1,4 +1,6 @@ // check that moves due to a closure capture give a special note +//@ run-rustfix +#![allow(unused_variables, unused_must_use, dead_code)] fn move_after_move(x: String) { || x; diff --git a/tests/ui/nll/closure-move-spans.stderr b/tests/ui/nll/closure-move-spans.stderr index 0446ef7b066..0b1da57605c 100644 --- a/tests/ui/nll/closure-move-spans.stderr +++ b/tests/ui/nll/closure-move-spans.stderr @@ -1,5 +1,5 @@ error[E0382]: use of moved value: `x` - --> $DIR/closure-move-spans.rs:5:13 + --> $DIR/closure-move-spans.rs:7:13 | LL | fn move_after_move(x: String) { | - move occurs because `x` has type `String`, which does not implement the `Copy` trait @@ -9,9 +9,14 @@ LL | || x; | value moved into closure here LL | let y = x; | ^ value used here after move + | +help: consider cloning the value if the performance cost is acceptable + | +LL | || x.clone(); + | ++++++++ error[E0382]: borrow of moved value: `x` - --> $DIR/closure-move-spans.rs:10:13 + --> $DIR/closure-move-spans.rs:12:13 | LL | fn borrow_after_move(x: String) { | - move occurs because `x` has type `String`, which does not implement the `Copy` trait @@ -21,9 +26,14 @@ LL | || x; | value moved into closure here LL | let y = &x; | ^^ value borrowed here after move + | +help: consider cloning the value if the performance cost is acceptable + | +LL | || x.clone(); + | ++++++++ error[E0382]: borrow of moved value: `x` - --> $DIR/closure-move-spans.rs:15:13 + --> $DIR/closure-move-spans.rs:17:13 | LL | fn borrow_mut_after_move(mut x: String) { | ----- move occurs because `x` has type `String`, which does not implement the `Copy` trait @@ -33,6 +43,11 @@ LL | || x; | value moved into closure here LL | let y = &mut x; | ^^^^^^ value borrowed here after move + | +help: consider cloning the value if the performance cost is acceptable + | +LL | || x.clone(); + | ++++++++ error: aborting due to 3 previous errors diff --git a/tests/ui/nll/closures-in-loops.stderr b/tests/ui/nll/closures-in-loops.stderr index 2c1008c516c..050b220e626 100644 --- a/tests/ui/nll/closures-in-loops.stderr +++ b/tests/ui/nll/closures-in-loops.stderr @@ -4,10 +4,16 @@ error[E0382]: use of moved value: `x` LL | fn repreated_move(x: String) { | - move occurs because `x` has type `String`, which does not implement the `Copy` trait LL | for i in 0..10 { + | -------------- inside of this loop LL | || x; | ^^ - use occurs due to use in closure | | | value moved into closure here, in previous iteration of loop + | +help: consider cloning the value if the performance cost is acceptable + | +LL | || x.clone(); + | ++++++++ error[E0499]: cannot borrow `x` as mutable more than once at a time --> $DIR/closures-in-loops.rs:13:16 diff --git a/tests/ui/nll/issue-21232-partial-init-and-use.stderr b/tests/ui/nll/issue-21232-partial-init-and-use.stderr index 97ed414b1ec..2aff375f0a7 100644 --- a/tests/ui/nll/issue-21232-partial-init-and-use.stderr +++ b/tests/ui/nll/issue-21232-partial-init-and-use.stderr @@ -27,6 +27,12 @@ LL | let mut s: S<B> = S::new(); drop(s); | move occurs because `s` has type `S<Box<u32>>`, which does not implement the `Copy` trait LL | s.x = 10; s.y = Box::new(20); | ^^^^^^^^ value partially assigned here after move + | +note: if `S<Box<u32>>` implemented `Clone`, you could clone the value + --> $DIR/issue-21232-partial-init-and-use.rs:15:1 + | +LL | struct S<Y> { + | ^^^^^^^^^^^ error[E0382]: assign to part of moved value: `t` --> $DIR/issue-21232-partial-init-and-use.rs:116:5 @@ -72,6 +78,12 @@ LL | let mut s: S<B> = S::new(); drop(s); | move occurs because `s` has type `S<Box<u32>>`, which does not implement the `Copy` trait LL | s.x = 10; | ^^^^^^^^ value partially assigned here after move + | +note: if `S<Box<u32>>` implemented `Clone`, you could clone the value + --> $DIR/issue-21232-partial-init-and-use.rs:15:1 + | +LL | struct S<Y> { + | ^^^^^^^^^^^ error[E0382]: assign to part of moved value: `t` --> $DIR/issue-21232-partial-init-and-use.rs:142:5 diff --git a/tests/ui/nll/issue-27282-move-match-input-into-guard.stderr b/tests/ui/nll/issue-27282-move-match-input-into-guard.stderr index ae797800457..39ec45b20ea 100644 --- a/tests/ui/nll/issue-27282-move-match-input-into-guard.stderr +++ b/tests/ui/nll/issue-27282-move-match-input-into-guard.stderr @@ -10,6 +10,11 @@ LL | _ if { (|| { let bar = b; *bar = false; })(); | -- - variable moved due to use in closure | | | value moved into closure here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | _ if { (|| { let bar = b.clone(); *bar = false; })(); + | ++++++++ error[E0382]: use of moved value: `b` --> $DIR/issue-27282-move-match-input-into-guard.rs:24:5 @@ -23,6 +28,11 @@ LL | (|| { let bar = b; *bar = false; })(); | -- - variable moved due to use in closure | | | value moved into closure here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | (|| { let bar = b.clone(); *bar = false; })(); + | ++++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/nll/issue-52059-report-when-borrow-and-drop-conflict.stderr b/tests/ui/nll/issue-52059-report-when-borrow-and-drop-conflict.stderr index 7f9cbc3c30a..f4e7869bf00 100644 --- a/tests/ui/nll/issue-52059-report-when-borrow-and-drop-conflict.stderr +++ b/tests/ui/nll/issue-52059-report-when-borrow-and-drop-conflict.stderr @@ -41,6 +41,10 @@ help: consider borrowing here | LL | let p = &s.url; p | + +help: consider cloning the value if the performance cost is acceptable + | +LL | let p = s.url.clone(); p + | ++++++++ error: aborting due to 4 previous errors diff --git a/tests/ui/nll/issue-52086.stderr b/tests/ui/nll/issue-52086.stderr index 3b2dae9b72b..e4f0c49e557 100644 --- a/tests/ui/nll/issue-52086.stderr +++ b/tests/ui/nll/issue-52086.stderr @@ -3,12 +3,22 @@ error[E0507]: cannot move out of an `Rc` | LL | drop(x.field); | ^^^^^^^ move occurs because value has type `Vec<i32>`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL | drop(x.field.clone()); + | ++++++++ error[E0507]: cannot move out of an `Arc` --> $DIR/issue-52086.rs:12:10 | LL | drop(y.field); | ^^^^^^^ move occurs because value has type `Vec<i32>`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL | drop(y.field.clone()); + | ++++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/nll/issue-52663-span-decl-captured-variable.stderr b/tests/ui/nll/issue-52663-span-decl-captured-variable.stderr index 587f3071027..fbaec8a6008 100644 --- a/tests/ui/nll/issue-52663-span-decl-captured-variable.stderr +++ b/tests/ui/nll/issue-52663-span-decl-captured-variable.stderr @@ -7,6 +7,11 @@ LL | expect_fn(|| drop(x.0)); | -- ^^^ move occurs because `x.0` has type `Vec<i32>`, which does not implement the `Copy` trait | | | captured by this `Fn` closure + | +help: consider cloning the value if the performance cost is acceptable + | +LL | expect_fn(|| drop(x.0.clone())); + | ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/nll/match-cfg-fake-edges.stderr b/tests/ui/nll/match-cfg-fake-edges.stderr index d692ded36fa..066e77b17fc 100644 --- a/tests/ui/nll/match-cfg-fake-edges.stderr +++ b/tests/ui/nll/match-cfg-fake-edges.stderr @@ -126,8 +126,8 @@ LL | _ if { x; false } => 2, | help: consider assigning a value | -LL | let x = 0; - | +++ +LL | let x = 42; + | ++++ error[E0381]: used binding `x` isn't initialized --> $DIR/match-cfg-fake-edges.rs:86:31 @@ -142,8 +142,8 @@ LL | _ if let Some(()) = { x; None } => 2, | help: consider assigning a value | -LL | let x = 0; - | +++ +LL | let x = 42; + | ++++ error[E0382]: use of moved value: `x` --> $DIR/match-cfg-fake-edges.rs:99:22 diff --git a/tests/ui/nll/match-on-borrowed.stderr b/tests/ui/nll/match-on-borrowed.stderr index 9273484565a..4e0b048fb4b 100644 --- a/tests/ui/nll/match-on-borrowed.stderr +++ b/tests/ui/nll/match-on-borrowed.stderr @@ -43,8 +43,8 @@ LL | match n {} | help: consider assigning a value | -LL | let n: Never = todo!(); - | +++++++++ +LL | let n: Never = value; + | +++++++ error: aborting due to 4 previous errors diff --git a/tests/ui/nll/move-errors.stderr b/tests/ui/nll/move-errors.stderr index 0d994ef29ba..842ecaf524b 100644 --- a/tests/ui/nll/move-errors.stderr +++ b/tests/ui/nll/move-errors.stderr @@ -4,6 +4,11 @@ error[E0507]: cannot move out of `*a` which is behind a shared reference LL | let b = *a; | ^^ move occurs because `*a` has type `A`, which does not implement the `Copy` trait | +note: if `A` implemented `Clone`, you could clone the value + --> $DIR/move-errors.rs:1:1 + | +LL | struct A(String); + | ^^^^^^^^ help: consider removing the dereference here | LL - let b = *a; @@ -19,6 +24,11 @@ LL | let b = a[0]; | cannot move out of here | move occurs because `a[_]` has type `A`, which does not implement the `Copy` trait | +note: if `A` implemented `Clone`, you could clone the value + --> $DIR/move-errors.rs:1:1 + | +LL | struct A(String); + | ^^^^^^^^ help: consider borrowing here | LL | let b = &a[0]; @@ -30,6 +40,11 @@ error[E0507]: cannot move out of `**r` which is behind a shared reference LL | let s = **r; | ^^^ move occurs because `**r` has type `A`, which does not implement the `Copy` trait | +note: if `A` implemented `Clone`, you could clone the value + --> $DIR/move-errors.rs:1:1 + | +LL | struct A(String); + | ^^^^^^^^ help: consider removing the dereference here | LL - let s = **r; @@ -42,6 +57,11 @@ error[E0507]: cannot move out of an `Rc` LL | let s = *r; | ^^ move occurs because value has type `A`, which does not implement the `Copy` trait | +note: if `A` implemented `Clone`, you could clone the value + --> $DIR/move-errors.rs:1:1 + | +LL | struct A(String); + | ^^^^^^^^ help: consider removing the dereference here | LL - let s = *r; @@ -57,6 +77,11 @@ LL | let a = [A("".to_string())][0]; | cannot move out of here | move occurs because value has type `A`, which does not implement the `Copy` trait | +note: if `A` implemented `Clone`, you could clone the value + --> $DIR/move-errors.rs:1:1 + | +LL | struct A(String); + | ^^^^^^^^ help: consider borrowing here | LL | let a = &[A("".to_string())][0]; @@ -96,6 +121,12 @@ error[E0507]: cannot move out of `*a` which is behind a shared reference | LL | b = *a; | ^^ move occurs because `*a` has type `A`, which does not implement the `Copy` trait + | +note: if `A` implemented `Clone`, you could clone the value + --> $DIR/move-errors.rs:1:1 + | +LL | struct A(String); + | ^^^^^^^^ error[E0508]: cannot move out of type `[B; 1]`, a non-copy array --> $DIR/move-errors.rs:74:11 diff --git a/tests/ui/once-cant-call-twice-on-heap.stderr b/tests/ui/once-cant-call-twice-on-heap.stderr index 33dd840dbc2..42697374115 100644 --- a/tests/ui/once-cant-call-twice-on-heap.stderr +++ b/tests/ui/once-cant-call-twice-on-heap.stderr @@ -8,15 +8,13 @@ LL | blk(); LL | blk(); | ^^^ value used here after move | -note: this value implements `FnOnce`, which causes it to be moved when called - --> $DIR/once-cant-call-twice-on-heap.rs:7:5 +note: `FnOnce` closures can only be called once + --> $DIR/once-cant-call-twice-on-heap.rs:6:10 | +LL | fn foo<F:FnOnce()>(blk: F) { + | ^^^^^^^^ `F` is made to be an `FnOnce` closure here LL | blk(); - | ^^^ -help: consider further restricting this bound - | -LL | fn foo<F:FnOnce() + Copy>(blk: F) { - | ++++++ + | ----- this value implements `FnOnce`, which causes it to be moved when called error: aborting due to 1 previous error diff --git a/tests/ui/parser/assoc/assoc-static-semantic-fail.stderr b/tests/ui/parser/assoc/assoc-static-semantic-fail.stderr index 8178bd22373..cc21df77353 100644 --- a/tests/ui/parser/assoc/assoc-static-semantic-fail.stderr +++ b/tests/ui/parser/assoc/assoc-static-semantic-fail.stderr @@ -138,7 +138,7 @@ error[E0449]: visibility qualifiers are not permitted here --> $DIR/assoc-static-semantic-fail.rs:32:5 | LL | pub(crate) default static TD: u8; - | ^^^^^^^^^^ + | ^^^^^^^^^^ help: remove the qualifier | = note: trait items always share the visibility of their trait @@ -162,7 +162,7 @@ error[E0449]: visibility qualifiers are not permitted here --> $DIR/assoc-static-semantic-fail.rs:47:5 | LL | pub default static TD: u8; - | ^^^ + | ^^^ help: remove the qualifier | = note: trait items always share the visibility of their trait diff --git a/tests/ui/parser/default.stderr b/tests/ui/parser/default.stderr index e6330f368d9..c420e5a774d 100644 --- a/tests/ui/parser/default.stderr +++ b/tests/ui/parser/default.stderr @@ -21,7 +21,7 @@ error[E0449]: visibility qualifiers are not permitted here --> $DIR/default.rs:17:5 | LL | pub default fn foo<T: Default>() -> T { - | ^^^ + | ^^^ help: remove the qualifier | = note: trait items always share the visibility of their trait diff --git a/tests/ui/parser/recover/recover-range-pats.stderr b/tests/ui/parser/recover/recover-range-pats.stderr index 7c5cc4777b6..2c0baf7e5f8 100644 --- a/tests/ui/parser/recover/recover-range-pats.stderr +++ b/tests/ui/parser/recover/recover-range-pats.stderr @@ -491,10 +491,6 @@ LL | mac2!(0, 1); = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html = note: the matched value is of type `i32` = note: this error originates in the macro `mac2` (in Nightly builds, run with -Z macro-backtrace for more info) -help: you might want to use `if let` to ignore the variants that aren't matched - | -LL | if let $e1..$e2; { todo!() } - | ++ +++++++++++ error[E0005]: refutable pattern in local binding --> $DIR/recover-range-pats.rs:138:17 @@ -509,10 +505,6 @@ LL | mac2!(0, 1); = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html = note: the matched value is of type `i32` = note: this error originates in the macro `mac2` (in Nightly builds, run with -Z macro-backtrace for more info) -help: you might want to use `if let` to ignore the variants that aren't matched - | -LL | if let $e1...$e2; { todo!() } - | ++ +++++++++++ error[E0005]: refutable pattern in local binding --> $DIR/recover-range-pats.rs:142:17 @@ -527,10 +519,6 @@ LL | mac2!(0, 1); = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html = note: the matched value is of type `i32` = note: this error originates in the macro `mac2` (in Nightly builds, run with -Z macro-backtrace for more info) -help: you might want to use `if let` to ignore the variants that aren't matched - | -LL | if let $e1..=$e2; { todo!() } - | ++ +++++++++++ error[E0005]: refutable pattern in local binding --> $DIR/recover-range-pats.rs:151:17 @@ -545,10 +533,6 @@ LL | mac!(0); = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html = note: the matched value is of type `i32` = note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info) -help: you might want to use `if let` to ignore the variant that isn't matched - | -LL | if let ..$e; { todo!() } - | ++ +++++++++++ error[E0005]: refutable pattern in local binding --> $DIR/recover-range-pats.rs:153:17 @@ -563,10 +547,6 @@ LL | mac!(0); = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html = note: the matched value is of type `i32` = note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info) -help: you might want to use `if let` to ignore the variant that isn't matched - | -LL | if let ...$e; { todo!() } - | ++ +++++++++++ error[E0005]: refutable pattern in local binding --> $DIR/recover-range-pats.rs:156:17 @@ -581,10 +561,6 @@ LL | mac!(0); = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html = note: the matched value is of type `i32` = note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info) -help: you might want to use `if let` to ignore the variant that isn't matched - | -LL | if let ..=$e; { todo!() } - | ++ +++++++++++ error[E0005]: refutable pattern in local binding --> $DIR/recover-range-pats.rs:158:17 @@ -599,10 +575,6 @@ LL | mac!(0); = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html = note: the matched value is of type `i32` = note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info) -help: you might want to use `if let` to ignore the variant that isn't matched - | -LL | if let $e..; { todo!() } - | ++ +++++++++++ error[E0005]: refutable pattern in local binding --> $DIR/recover-range-pats.rs:160:17 @@ -617,10 +589,6 @@ LL | mac!(0); = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html = note: the matched value is of type `i32` = note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info) -help: you might want to use `if let` to ignore the variant that isn't matched - | -LL | if let $e...; { todo!() } - | ++ +++++++++++ error[E0005]: refutable pattern in local binding --> $DIR/recover-range-pats.rs:162:17 @@ -635,10 +603,6 @@ LL | mac!(0); = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html = note: the matched value is of type `i32` = note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info) -help: you might want to use `if let` to ignore the variant that isn't matched - | -LL | if let $e..=; { todo!() } - | ++ +++++++++++ error: aborting due to 69 previous errors diff --git a/tests/ui/parser/trait-pub-assoc-const.stderr b/tests/ui/parser/trait-pub-assoc-const.stderr index 436f6a3909c..1bace786b21 100644 --- a/tests/ui/parser/trait-pub-assoc-const.stderr +++ b/tests/ui/parser/trait-pub-assoc-const.stderr @@ -2,7 +2,7 @@ error[E0449]: visibility qualifiers are not permitted here --> $DIR/trait-pub-assoc-const.rs:2:5 | LL | pub const Foo: u32; - | ^^^ + | ^^^ help: remove the qualifier | = note: trait items always share the visibility of their trait diff --git a/tests/ui/parser/trait-pub-assoc-ty.stderr b/tests/ui/parser/trait-pub-assoc-ty.stderr index 279e3a95354..28e05bdc630 100644 --- a/tests/ui/parser/trait-pub-assoc-ty.stderr +++ b/tests/ui/parser/trait-pub-assoc-ty.stderr @@ -2,7 +2,7 @@ error[E0449]: visibility qualifiers are not permitted here --> $DIR/trait-pub-assoc-ty.rs:2:5 | LL | pub type Foo; - | ^^^ + | ^^^ help: remove the qualifier | = note: trait items always share the visibility of their trait diff --git a/tests/ui/parser/trait-pub-method.stderr b/tests/ui/parser/trait-pub-method.stderr index ee8b6f7cb62..cc1ba0eaaea 100644 --- a/tests/ui/parser/trait-pub-method.stderr +++ b/tests/ui/parser/trait-pub-method.stderr @@ -2,7 +2,7 @@ error[E0449]: visibility qualifiers are not permitted here --> $DIR/trait-pub-method.rs:2:5 | LL | pub fn foo(); - | ^^^ + | ^^^ help: remove the qualifier | = note: trait items always share the visibility of their trait diff --git a/tests/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.stderr b/tests/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.stderr index 9359244c6eb..00964cb8336 100644 --- a/tests/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.stderr +++ b/tests/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.stderr @@ -333,6 +333,11 @@ LL | ref a @ Ok(ref mut b) | ref a @ Err(ref mut b) if { drop(b); false | ^ move occurs because `b` has type `&mut U`, which does not implement the `Copy` trait | = note: variables bound in patterns cannot be moved from until after the end of the pattern guard +note: if `U` implemented `Clone`, you could clone the value + --> $DIR/borrowck-pat-ref-mut-and-ref.rs:17:5 + | +LL | struct U; + | ^^^^^^^^ error[E0507]: cannot move out of `b` in pattern guard --> $DIR/borrowck-pat-ref-mut-and-ref.rs:103:66 @@ -341,6 +346,11 @@ LL | ref a @ Ok(ref mut b) | ref a @ Err(ref mut b) if { drop(b); false | ^ move occurs because `b` has type `&mut U`, which does not implement the `Copy` trait | = note: variables bound in patterns cannot be moved from until after the end of the pattern guard +note: if `U` implemented `Clone`, you could clone the value + --> $DIR/borrowck-pat-ref-mut-and-ref.rs:17:5 + | +LL | struct U; + | ^^^^^^^^ = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0507]: cannot move out of `a` in pattern guard diff --git a/tests/ui/precondition-checks/cfg-ub-checks-default.rs b/tests/ui/precondition-checks/cfg-ub-checks-default.rs index 57a19523147..b672589d02a 100644 --- a/tests/ui/precondition-checks/cfg-ub-checks-default.rs +++ b/tests/ui/precondition-checks/cfg-ub-checks-default.rs @@ -1,5 +1,5 @@ //@ run-pass -//@ revisions YES NO +//@ revisions: YES NO //@ [YES] compile-flags: -Cdebug-assertions=yes //@ [NO] compile-flags: -Cdebug-assertions=no diff --git a/tests/ui/privacy/issue-113860-1.stderr b/tests/ui/privacy/issue-113860-1.stderr index c33ce26f0f6..c05452fb51c 100644 --- a/tests/ui/privacy/issue-113860-1.stderr +++ b/tests/ui/privacy/issue-113860-1.stderr @@ -2,7 +2,7 @@ error[E0449]: visibility qualifiers are not permitted here --> $DIR/issue-113860-1.rs:12:5 | LL | pub(self) fn fun() {} - | ^^^^^^^^^ + | ^^^^^^^^^ help: remove the qualifier | = note: trait items always share the visibility of their trait diff --git a/tests/ui/privacy/issue-113860-2.stderr b/tests/ui/privacy/issue-113860-2.stderr index 6748bc27668..c53c490ca1e 100644 --- a/tests/ui/privacy/issue-113860-2.stderr +++ b/tests/ui/privacy/issue-113860-2.stderr @@ -2,7 +2,7 @@ error[E0449]: visibility qualifiers are not permitted here --> $DIR/issue-113860-2.rs:12:5 | LL | pub(self) type X = Self; - | ^^^^^^^^^ + | ^^^^^^^^^ help: remove the qualifier | = note: trait items always share the visibility of their trait diff --git a/tests/ui/privacy/issue-113860.stderr b/tests/ui/privacy/issue-113860.stderr index 3204f4ff916..d813b740ac5 100644 --- a/tests/ui/privacy/issue-113860.stderr +++ b/tests/ui/privacy/issue-113860.stderr @@ -2,7 +2,7 @@ error[E0449]: visibility qualifiers are not permitted here --> $DIR/issue-113860.rs:12:5 | LL | pub(self) const X: u32 = 3; - | ^^^^^^^^^ + | ^^^^^^^^^ help: remove the qualifier | = note: trait items always share the visibility of their trait diff --git a/tests/ui/privacy/issue-29161.stderr b/tests/ui/privacy/issue-29161.stderr index 1a6c80499a1..f8911cb09c1 100644 --- a/tests/ui/privacy/issue-29161.stderr +++ b/tests/ui/privacy/issue-29161.stderr @@ -2,7 +2,7 @@ error[E0449]: visibility qualifiers are not permitted here --> $DIR/issue-29161.rs:5:9 | LL | pub fn default() -> A { - | ^^^ + | ^^^ help: remove the qualifier | = note: trait items always share the visibility of their trait diff --git a/tests/ui/privacy/priv-in-bad-locations.stderr b/tests/ui/privacy/priv-in-bad-locations.stderr index 70dab5bfe13..93a0eabb914 100644 --- a/tests/ui/privacy/priv-in-bad-locations.stderr +++ b/tests/ui/privacy/priv-in-bad-locations.stderr @@ -2,7 +2,7 @@ error[E0449]: visibility qualifiers are not permitted here --> $DIR/priv-in-bad-locations.rs:1:1 | LL | pub extern "C" { - | ^^^ + | ^^^ help: remove the qualifier | = note: place qualifiers on individual foreign items instead @@ -10,7 +10,7 @@ error[E0449]: visibility qualifiers are not permitted here --> $DIR/priv-in-bad-locations.rs:11:1 | LL | pub impl B {} - | ^^^ + | ^^^ help: remove the qualifier | = note: place qualifiers on individual impl items instead @@ -18,7 +18,7 @@ error[E0449]: visibility qualifiers are not permitted here --> $DIR/priv-in-bad-locations.rs:13:1 | LL | pub impl A for B { - | ^^^ + | ^^^ help: remove the qualifier | = note: trait items always share the visibility of their trait @@ -26,7 +26,7 @@ error[E0449]: visibility qualifiers are not permitted here --> $DIR/priv-in-bad-locations.rs:14:5 | LL | pub fn foo(&self) {} - | ^^^ + | ^^^ help: remove the qualifier | = note: trait items always share the visibility of their trait diff --git a/tests/ui/privacy/privacy-sanity.stderr b/tests/ui/privacy/privacy-sanity.stderr index a537f8c1901..0acb05cbaba 100644 --- a/tests/ui/privacy/privacy-sanity.stderr +++ b/tests/ui/privacy/privacy-sanity.stderr @@ -2,7 +2,7 @@ error[E0449]: visibility qualifiers are not permitted here --> $DIR/privacy-sanity.rs:13:1 | LL | pub impl Tr for S { - | ^^^ + | ^^^ help: remove the qualifier | = note: trait items always share the visibility of their trait @@ -10,7 +10,7 @@ error[E0449]: visibility qualifiers are not permitted here --> $DIR/privacy-sanity.rs:14:5 | LL | pub fn f() {} - | ^^^ + | ^^^ help: remove the qualifier | = note: trait items always share the visibility of their trait @@ -18,7 +18,7 @@ error[E0449]: visibility qualifiers are not permitted here --> $DIR/privacy-sanity.rs:15:5 | LL | pub const C: u8 = 0; - | ^^^ + | ^^^ help: remove the qualifier | = note: trait items always share the visibility of their trait @@ -26,7 +26,7 @@ error[E0449]: visibility qualifiers are not permitted here --> $DIR/privacy-sanity.rs:16:5 | LL | pub type T = u8; - | ^^^ + | ^^^ help: remove the qualifier | = note: trait items always share the visibility of their trait @@ -34,7 +34,7 @@ error[E0449]: visibility qualifiers are not permitted here --> $DIR/privacy-sanity.rs:18:1 | LL | pub impl S { - | ^^^ + | ^^^ help: remove the qualifier | = note: place qualifiers on individual impl items instead @@ -42,7 +42,7 @@ error[E0449]: visibility qualifiers are not permitted here --> $DIR/privacy-sanity.rs:23:1 | LL | pub extern "C" { - | ^^^ + | ^^^ help: remove the qualifier | = note: place qualifiers on individual foreign items instead @@ -50,7 +50,7 @@ error[E0449]: visibility qualifiers are not permitted here --> $DIR/privacy-sanity.rs:39:5 | LL | pub impl Tr for S { - | ^^^ + | ^^^ help: remove the qualifier | = note: trait items always share the visibility of their trait @@ -58,7 +58,7 @@ error[E0449]: visibility qualifiers are not permitted here --> $DIR/privacy-sanity.rs:40:9 | LL | pub fn f() {} - | ^^^ + | ^^^ help: remove the qualifier | = note: trait items always share the visibility of their trait @@ -66,7 +66,7 @@ error[E0449]: visibility qualifiers are not permitted here --> $DIR/privacy-sanity.rs:41:9 | LL | pub const C: u8 = 0; - | ^^^ + | ^^^ help: remove the qualifier | = note: trait items always share the visibility of their trait @@ -74,7 +74,7 @@ error[E0449]: visibility qualifiers are not permitted here --> $DIR/privacy-sanity.rs:42:9 | LL | pub type T = u8; - | ^^^ + | ^^^ help: remove the qualifier | = note: trait items always share the visibility of their trait @@ -82,7 +82,7 @@ error[E0449]: visibility qualifiers are not permitted here --> $DIR/privacy-sanity.rs:44:5 | LL | pub impl S { - | ^^^ + | ^^^ help: remove the qualifier | = note: place qualifiers on individual impl items instead @@ -90,7 +90,7 @@ error[E0449]: visibility qualifiers are not permitted here --> $DIR/privacy-sanity.rs:49:5 | LL | pub extern "C" { - | ^^^ + | ^^^ help: remove the qualifier | = note: place qualifiers on individual foreign items instead @@ -98,7 +98,7 @@ error[E0449]: visibility qualifiers are not permitted here --> $DIR/privacy-sanity.rs:68:5 | LL | pub impl Tr for S { - | ^^^ + | ^^^ help: remove the qualifier | = note: trait items always share the visibility of their trait @@ -106,7 +106,7 @@ error[E0449]: visibility qualifiers are not permitted here --> $DIR/privacy-sanity.rs:69:9 | LL | pub fn f() {} - | ^^^ + | ^^^ help: remove the qualifier | = note: trait items always share the visibility of their trait @@ -114,7 +114,7 @@ error[E0449]: visibility qualifiers are not permitted here --> $DIR/privacy-sanity.rs:70:9 | LL | pub const C: u8 = 0; - | ^^^ + | ^^^ help: remove the qualifier | = note: trait items always share the visibility of their trait @@ -122,7 +122,7 @@ error[E0449]: visibility qualifiers are not permitted here --> $DIR/privacy-sanity.rs:71:9 | LL | pub type T = u8; - | ^^^ + | ^^^ help: remove the qualifier | = note: trait items always share the visibility of their trait @@ -130,7 +130,7 @@ error[E0449]: visibility qualifiers are not permitted here --> $DIR/privacy-sanity.rs:73:5 | LL | pub impl S { - | ^^^ + | ^^^ help: remove the qualifier | = note: place qualifiers on individual impl items instead @@ -138,7 +138,7 @@ error[E0449]: visibility qualifiers are not permitted here --> $DIR/privacy-sanity.rs:78:5 | LL | pub extern "C" { - | ^^^ + | ^^^ help: remove the qualifier | = note: place qualifiers on individual foreign items instead diff --git a/tests/ui/privacy/useless-pub.stderr b/tests/ui/privacy/useless-pub.stderr index 73497e3fed5..7d064c12a09 100644 --- a/tests/ui/privacy/useless-pub.stderr +++ b/tests/ui/privacy/useless-pub.stderr @@ -2,7 +2,7 @@ error[E0449]: visibility qualifiers are not permitted here --> $DIR/useless-pub.rs:8:5 | LL | pub fn foo(&self) {} - | ^^^ + | ^^^ help: remove the qualifier | = note: trait items always share the visibility of their trait @@ -10,7 +10,7 @@ error[E0449]: visibility qualifiers are not permitted here --> $DIR/useless-pub.rs:12:10 | LL | V1 { pub f: i32 }, - | ^^^ + | ^^^ help: remove the qualifier | = note: enum variants and their fields always share the visibility of the enum they are in @@ -18,7 +18,7 @@ error[E0449]: visibility qualifiers are not permitted here --> $DIR/useless-pub.rs:13:8 | LL | V2(pub i32), - | ^^^ + | ^^^ help: remove the qualifier | = note: enum variants and their fields always share the visibility of the enum they are in diff --git a/tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-across-arms.stderr b/tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-across-arms.stderr index a749361bf30..0ab70c5ae8a 100644 --- a/tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-across-arms.stderr +++ b/tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-across-arms.stderr @@ -5,6 +5,10 @@ LL | VecWrapper::A(v) if { drop(v); false } => 1, | ^ move occurs because `v` has type `Vec<i32>`, which does not implement the `Copy` trait | = note: variables bound in patterns cannot be moved from until after the end of the pattern guard +help: consider cloning the value if the performance cost is acceptable + | +LL | VecWrapper::A(v) if { drop(v.clone()); false } => 1, + | ++++++++ error[E0507]: cannot move out of `v` in pattern guard --> $DIR/rfc-reject-double-move-across-arms.rs:15:51 @@ -13,6 +17,10 @@ LL | VecWrapper::A(v) if let Some(()) = { drop(v); None } => 1, | ^ move occurs because `v` has type `Vec<i32>`, which does not implement the `Copy` trait | = note: variables bound in patterns cannot be moved from until after the end of the pattern guard +help: consider cloning the value if the performance cost is acceptable + | +LL | VecWrapper::A(v) if let Some(()) = { drop(v.clone()); None } => 1, + | ++++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-in-first-arm.stderr b/tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-in-first-arm.stderr index 9285492b224..c261f994283 100644 --- a/tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-in-first-arm.stderr +++ b/tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-in-first-arm.stderr @@ -5,6 +5,10 @@ LL | A { a: v } if { drop(v); true } => v, | ^ move occurs because `v` has type `Box<i32>`, which does not implement the `Copy` trait | = note: variables bound in patterns cannot be moved from until after the end of the pattern guard +help: consider cloning the value if the performance cost is acceptable + | +LL | A { a: v } if { drop(v.clone()); true } => v, + | ++++++++ error[E0507]: cannot move out of `v` in pattern guard --> $DIR/rfc-reject-double-move-in-first-arm.rs:17:45 @@ -13,6 +17,10 @@ LL | A { a: v } if let Some(()) = { drop(v); Some(()) } => v, | ^ move occurs because `v` has type `Box<i32>`, which does not implement the `Copy` trait | = note: variables bound in patterns cannot be moved from until after the end of the pattern guard +help: consider cloning the value if the performance cost is acceptable + | +LL | A { a: v } if let Some(()) = { drop(v.clone()); Some(()) } => v, + | ++++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr b/tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr index 29a606c4f01..f37dc320fa3 100644 --- a/tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr +++ b/tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr @@ -43,6 +43,11 @@ LL | f(Box::new(|a| { LL | LL | foo(f); | ^ move occurs because `f` has type `{closure@$DIR/borrowck-call-is-borrow-issue-12224.rs:52:17: 52:58}`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL | foo(f.clone()); + | ++++++++ error[E0505]: cannot move out of `f` because it is borrowed --> $DIR/borrowck-call-is-borrow-issue-12224.rs:55:16 @@ -57,6 +62,11 @@ LL | f(Box::new(|a| { LL | LL | foo(f); | - move occurs due to use in closure + | +help: consider cloning the value if the performance cost is acceptable + | +LL | f.clone()(Box::new(|a| { + | ++++++++ error: aborting due to 5 previous errors diff --git a/tests/ui/span/send-is-not-static-std-sync.stderr b/tests/ui/span/send-is-not-static-std-sync.stderr index 46534b39168..50b8ffe0114 100644 --- a/tests/ui/span/send-is-not-static-std-sync.stderr +++ b/tests/ui/span/send-is-not-static-std-sync.stderr @@ -11,6 +11,12 @@ LL | drop(y); ... LL | *lock.lock().unwrap() = &z; | ---- borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL - *lock.lock().unwrap() = &*y; +LL + *lock.lock().unwrap() = y.clone(); + | error[E0597]: `z` does not live long enough --> $DIR/send-is-not-static-std-sync.rs:16:33 @@ -38,6 +44,12 @@ LL | drop(y); ... LL | *lock.write().unwrap() = &z; | ---- borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL - *lock.write().unwrap() = &*y; +LL + *lock.write().unwrap() = y.clone(); + | error[E0597]: `z` does not live long enough --> $DIR/send-is-not-static-std-sync.rs:30:34 @@ -65,6 +77,12 @@ LL | drop(y); ... LL | tx.send(&z).unwrap(); | -- borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL - tx.send(&*y); +LL + tx.send(y.clone()); + | error[E0597]: `z` does not live long enough --> $DIR/send-is-not-static-std-sync.rs:46:17 diff --git a/tests/ui/suggestions/borrow-for-loop-head.stderr b/tests/ui/suggestions/borrow-for-loop-head.stderr index 0f179438a12..a8de9986c31 100644 --- a/tests/ui/suggestions/borrow-for-loop-head.stderr +++ b/tests/ui/suggestions/borrow-for-loop-head.stderr @@ -7,6 +7,12 @@ LL | for i in &a { | -- borrow of `a` occurs here LL | for j in a { | ^ move out of `a` occurs here + | +help: consider cloning the value if the performance cost is acceptable + | +LL - for i in &a { +LL + for i in a.clone() { + | error[E0382]: use of moved value: `a` --> $DIR/borrow-for-loop-head.rs:4:18 diff --git a/tests/ui/suggestions/deref-path-method.stderr b/tests/ui/suggestions/deref-path-method.stderr index b27d9aef066..bfcc2307fd7 100644 --- a/tests/ui/suggestions/deref-path-method.stderr +++ b/tests/ui/suggestions/deref-path-method.stderr @@ -9,7 +9,7 @@ note: if you're trying to build a new `Vec<_, _>` consider using one of the foll Vec::<T>::with_capacity Vec::<T>::try_with_capacity Vec::<T>::from_raw_parts - and 4 others + and 6 others --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL help: the function `contains` is implemented on `[_]` | diff --git a/tests/ui/suggestions/for-i-in-vec.fixed b/tests/ui/suggestions/for-i-in-vec.fixed index f266e80bcfa..17ae6fb5ab7 100644 --- a/tests/ui/suggestions/for-i-in-vec.fixed +++ b/tests/ui/suggestions/for-i-in-vec.fixed @@ -8,9 +8,9 @@ struct Foo { impl Foo { fn bar(&self) { - for _ in &self.v { //~ ERROR cannot move out of `self.v` which is behind a shared reference + for _ in &self.v.clone() { //~ ERROR cannot move out of `self.v` which is behind a shared reference } - for _ in &self.h { //~ ERROR cannot move out of `self.h` which is behind a shared reference + for _ in &self.h.clone() { //~ ERROR cannot move out of `self.h` which is behind a shared reference } } } @@ -18,7 +18,7 @@ impl Foo { const LOADERS: &Vec<&'static u8> = &Vec::new(); pub fn break_code() -> Option<&'static u8> { - for loader in &*LOADERS { //~ ERROR cannot move out of a shared reference + for loader in &LOADERS.clone() { //~ ERROR cannot move out of a shared reference return Some(loader); } None diff --git a/tests/ui/suggestions/for-i-in-vec.stderr b/tests/ui/suggestions/for-i-in-vec.stderr index c5b81e6b871..64eb4f8bd23 100644 --- a/tests/ui/suggestions/for-i-in-vec.stderr +++ b/tests/ui/suggestions/for-i-in-vec.stderr @@ -13,6 +13,10 @@ help: consider iterating over a slice of the `Vec<u32>`'s content to avoid movin | LL | for _ in &self.v { | + +help: consider cloning the value if the performance cost is acceptable + | +LL | for _ in self.v.clone() { + | ++++++++ error[E0507]: cannot move out of `self.h` which is behind a shared reference --> $DIR/for-i-in-vec.rs:13:18 @@ -27,6 +31,10 @@ help: consider iterating over a slice of the `HashMap<i32, i32>`'s content to av | LL | for _ in &self.h { | + +help: consider cloning the value if the performance cost is acceptable + | +LL | for _ in self.h.clone() { + | ++++++++ error[E0507]: cannot move out of a shared reference --> $DIR/for-i-in-vec.rs:21:19 @@ -43,6 +51,11 @@ help: consider iterating over a slice of the `Vec<&u8>`'s content to avoid movin | LL | for loader in &*LOADERS { | + +help: consider cloning the value if the performance cost is acceptable + | +LL - for loader in *LOADERS { +LL + for loader in LOADERS.clone() { + | error: aborting due to 3 previous errors diff --git a/tests/ui/suggestions/issue-68049-1.stderr b/tests/ui/suggestions/issue-68049-1.stderr index 760f83d548f..4e683b75c48 100644 --- a/tests/ui/suggestions/issue-68049-1.stderr +++ b/tests/ui/suggestions/issue-68049-1.stderr @@ -1,6 +1,8 @@ error[E0594]: cannot assign to `self.0`, which is behind a `&` reference --> $DIR/issue-68049-1.rs:7:9 | +LL | unsafe fn alloc(&self, _layout: Layout) -> *mut u8 { + | ----- this is an immutable reference LL | self.0 += 1; | ^^^^^^^^^^^ `self` is a `&` reference, so the data it refers to cannot be written diff --git a/tests/ui/suggestions/issue-68049-2.rs b/tests/ui/suggestions/issue-68049-2.rs index 1c3430c14e9..496a1299dcf 100644 --- a/tests/ui/suggestions/issue-68049-2.rs +++ b/tests/ui/suggestions/issue-68049-2.rs @@ -1,11 +1,11 @@ trait Hello { - fn example(&self, input: &i32); // should suggest here + fn example(&self, input: &i32); } struct Test1(i32); impl Hello for Test1 { - fn example(&self, input: &i32) { // should not suggest here + fn example(&self, input: &i32) { *input = self.0; //~ ERROR cannot assign } } @@ -13,7 +13,7 @@ impl Hello for Test1 { struct Test2(i32); impl Hello for Test2 { - fn example(&self, input: &i32) { // should not suggest here + fn example(&self, input: &i32) { self.0 += *input; //~ ERROR cannot assign } } diff --git a/tests/ui/suggestions/issue-68049-2.stderr b/tests/ui/suggestions/issue-68049-2.stderr index 6f3c78443f8..449ecabeb7f 100644 --- a/tests/ui/suggestions/issue-68049-2.stderr +++ b/tests/ui/suggestions/issue-68049-2.stderr @@ -4,9 +4,9 @@ error[E0594]: cannot assign to `*input`, which is behind a `&` reference LL | *input = self.0; | ^^^^^^^^^^^^^^^ `input` is a `&` reference, so the data it refers to cannot be written | -help: consider changing this to be a mutable reference +help: consider changing this to be a mutable reference in the `impl` method and the `trait` definition | -LL | fn example(&self, input: &mut i32) { // should not suggest here +LL | fn example(&self, input: &mut i32) { | +++ error[E0594]: cannot assign to `self.0`, which is behind a `&` reference @@ -15,10 +15,14 @@ error[E0594]: cannot assign to `self.0`, which is behind a `&` reference LL | self.0 += *input; | ^^^^^^^^^^^^^^^^ `self` is a `&` reference, so the data it refers to cannot be written | -help: consider changing this to be a mutable reference +help: consider changing this to be a mutable reference in the `impl` method and the `trait` definition + | +LL ~ fn example(&mut self, input: &i32); +LL | } + ... +LL | impl Hello for Test2 { +LL ~ fn example(&mut self, input: &i32) { | -LL | fn example(&mut self, input: &i32); // should suggest here - | ~~~~~~~~~ error: aborting due to 2 previous errors diff --git a/tests/ui/suggestions/option-content-move.fixed b/tests/ui/suggestions/option-content-move.fixed index fbed486cef7..4a5a9483c20 100644 --- a/tests/ui/suggestions/option-content-move.fixed +++ b/tests/ui/suggestions/option-content-move.fixed @@ -7,7 +7,7 @@ impl LipogramCorpora { pub fn validate_all(&mut self) -> Result<(), char> { for selection in &self.selections { if selection.1.is_some() { - if <Option<String> as Clone>::clone(&selection.1).unwrap().contains(selection.0) { + if <Option<String> as Clone>::clone(&selection.1.clone()).unwrap().contains(selection.0) { //~^ ERROR cannot move out of `selection.1` return Err(selection.0); } @@ -25,7 +25,7 @@ impl LipogramCorpora2 { pub fn validate_all(&mut self) -> Result<(), char> { for selection in &self.selections { if selection.1.is_ok() { - if <Result<String, String> as Clone>::clone(&selection.1).unwrap().contains(selection.0) { + if <Result<String, String> as Clone>::clone(&selection.1.clone()).unwrap().contains(selection.0) { //~^ ERROR cannot move out of `selection.1` return Err(selection.0); } diff --git a/tests/ui/suggestions/option-content-move.stderr b/tests/ui/suggestions/option-content-move.stderr index e5de150275d..a382a04344a 100644 --- a/tests/ui/suggestions/option-content-move.stderr +++ b/tests/ui/suggestions/option-content-move.stderr @@ -13,6 +13,10 @@ help: you can `clone` the value and consume it, but this might not be your desir | LL | if <Option<String> as Clone>::clone(&selection.1).unwrap().contains(selection.0) { | ++++++++++++++++++++++++++++++++++ + +help: consider cloning the value if the performance cost is acceptable + | +LL | if selection.1.clone().unwrap().contains(selection.0) { + | ++++++++ error[E0507]: cannot move out of `selection.1` which is behind a shared reference --> $DIR/option-content-move.rs:28:20 @@ -29,6 +33,10 @@ help: you can `clone` the value and consume it, but this might not be your desir | LL | if <Result<String, String> as Clone>::clone(&selection.1).unwrap().contains(selection.0) { | ++++++++++++++++++++++++++++++++++++++++++ + +help: consider cloning the value if the performance cost is acceptable + | +LL | if selection.1.clone().unwrap().contains(selection.0) { + | ++++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/suggestions/return-closures.stderr b/tests/ui/suggestions/return-closures.stderr index 97c13200ac3..ef1f50b8a6c 100644 --- a/tests/ui/suggestions/return-closures.stderr +++ b/tests/ui/suggestions/return-closures.stderr @@ -2,7 +2,7 @@ error[E0308]: mismatched types --> $DIR/return-closures.rs:3:5 | LL | fn foo() { - | - help: try adding a return type: `-> impl for<'a> Fn(&'a i32) -> i32` + | - help: try adding a return type: `-> impl FnOnce(&i32) -> i32` LL | LL | |x: &i32| 1i32 | ^^^^^^^^^^^^^^ expected `()`, found closure diff --git a/tests/ui/traits/copy-guessing.rs b/tests/ui/traits/copy-guessing.rs index af25010e3bd..0ffa8249789 100644 --- a/tests/ui/traits/copy-guessing.rs +++ b/tests/ui/traits/copy-guessing.rs @@ -18,7 +18,7 @@ fn assert_impls_fn<R,T: Fn()->R>(_: &T){} fn main() { let n = None; - //~^ ERROR type annotations needed for `Option<T>` + //~^ ERROR type annotations needed for `Option<_>` let e = S(&n); let f = || { // S being copy is critical for this to work diff --git a/tests/ui/traits/copy-guessing.stderr b/tests/ui/traits/copy-guessing.stderr index 750140c017c..cae91579ee0 100644 --- a/tests/ui/traits/copy-guessing.stderr +++ b/tests/ui/traits/copy-guessing.stderr @@ -1,4 +1,4 @@ -error[E0282]: type annotations needed for `Option<T>` +error[E0282]: type annotations needed for `Option<_>` --> $DIR/copy-guessing.rs:20:9 | LL | let n = None; diff --git a/tests/ui/traits/issue-77982.stderr b/tests/ui/traits/issue-77982.stderr index 5be8d2f4b32..0f4b3c3c877 100644 --- a/tests/ui/traits/issue-77982.stderr +++ b/tests/ui/traits/issue-77982.stderr @@ -55,7 +55,7 @@ help: try using a fully qualified path to specify the expected types LL | let ips: Vec<_> = (0..100_000).map(|_| u32::from(<u32 as Into<T>>::into(0u32))).collect(); | +++++++++++++++++++++++ ~ -error[E0283]: type annotations needed for `Box<T>` +error[E0283]: type annotations needed for `Box<_>` --> $DIR/issue-77982.rs:37:9 | LL | let _ = ().foo(); @@ -73,7 +73,7 @@ help: consider giving this pattern a type, where the type for type parameter `T` LL | let _: Box<T> = ().foo(); | ++++++++ -error[E0283]: type annotations needed for `Box<T>` +error[E0283]: type annotations needed for `Box<_>` --> $DIR/issue-77982.rs:41:9 | LL | let _ = (&()).bar(); diff --git a/tests/ui/trivial-bounds/trivial-bounds-leak-copy.stderr b/tests/ui/trivial-bounds/trivial-bounds-leak-copy.stderr index e48d48a7271..4efb883ac74 100644 --- a/tests/ui/trivial-bounds/trivial-bounds-leak-copy.stderr +++ b/tests/ui/trivial-bounds/trivial-bounds-leak-copy.stderr @@ -3,6 +3,12 @@ error[E0507]: cannot move out of `*t` which is behind a shared reference | LL | *t | ^^ move occurs because `*t` has type `String`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL - *t +LL + t.clone() + | error: aborting due to 1 previous error diff --git a/tests/ui/try-block/try-block-bad-lifetime.stderr b/tests/ui/try-block/try-block-bad-lifetime.stderr index 28941cb0a9e..6f693295357 100644 --- a/tests/ui/try-block/try-block-bad-lifetime.stderr +++ b/tests/ui/try-block/try-block-bad-lifetime.stderr @@ -34,6 +34,11 @@ LL | Err(k) ?; ... LL | ::std::mem::drop(k); | ^ value used here after move + | +help: consider cloning the value if the performance cost is acceptable + | +LL | Err(k.clone()) ?; + | ++++++++ error[E0506]: cannot assign to `i` because it is borrowed --> $DIR/try-block-bad-lifetime.rs:32:9 diff --git a/tests/ui/type-inference/or_else-multiple-type-params.stderr b/tests/ui/type-inference/or_else-multiple-type-params.stderr index d1bbe308ed3..3176a2d490e 100644 --- a/tests/ui/type-inference/or_else-multiple-type-params.stderr +++ b/tests/ui/type-inference/or_else-multiple-type-params.stderr @@ -1,4 +1,4 @@ -error[E0282]: type annotations needed for `Result<Child, F>` +error[E0282]: type annotations needed for `Result<Child, _>` --> $DIR/or_else-multiple-type-params.rs:7:18 | LL | .or_else(|err| { diff --git a/tests/ui/type/type-error-break-tail.stderr b/tests/ui/type/type-error-break-tail.stderr index 5ef522fee2a..81f8f52428d 100644 --- a/tests/ui/type/type-error-break-tail.stderr +++ b/tests/ui/type/type-error-break-tail.stderr @@ -8,7 +8,7 @@ LL | loop { LL | if false { break; } | ^^^^^ expected `i32`, found `()` | -help: give it a value of the expected type +help: give the `break` a value of the expected type | LL | if false { break 42; } | ++ diff --git a/tests/ui/typeck/issue-112007-leaked-writeln-macro-internals.fixed b/tests/ui/typeck/issue-112007-leaked-writeln-macro-internals.fixed deleted file mode 100644 index dcb256de18f..00000000000 --- a/tests/ui/typeck/issue-112007-leaked-writeln-macro-internals.fixed +++ /dev/null @@ -1,37 +0,0 @@ -//@ run-rustfix -#![allow(dead_code)] - -// https://github.com/rust-lang/rust/issues/112007 -fn bug_report<W: std::fmt::Write>(w: &mut W) -> std::fmt::Result { - if true { - writeln!(w, "`;?` here ->")?; - } else { - return writeln!(w, "but not here"); - //~^ ERROR mismatched types - }; - Ok(()) -} - -macro_rules! baz { - ($w: expr) => { - bar!($w) - } -} - -macro_rules! bar { - ($w: expr) => { - writeln!($w, "but not here") - //~^ ERROR mismatched types - } -} - -fn foo<W: std::fmt::Write>(w: &mut W) -> std::fmt::Result { - if true { - writeln!(w, "`;?` here ->")?; - } else { - return baz!(w); - }; - Ok(()) -} - -fn main() {} diff --git a/tests/ui/typeck/issue-112007-leaked-writeln-macro-internals.rs b/tests/ui/typeck/issue-112007-leaked-writeln-macro-internals.rs index 58cd6cbf20c..7ec9f0d4cdb 100644 --- a/tests/ui/typeck/issue-112007-leaked-writeln-macro-internals.rs +++ b/tests/ui/typeck/issue-112007-leaked-writeln-macro-internals.rs @@ -1,8 +1,16 @@ -//@ run-rustfix -#![allow(dead_code)] +// Check that we don't leak stdlib implementation details through suggestions. +// Also check that the suggestion provided tries as hard as it can to see through local macros. +// +// FIXME(jieyouxu): this test is NOT run-rustfix because this test contains conflicting +// MaybeIncorrect suggestions: +// +// 1. `return ... ;` +// 2. `?` +// +// when the suggestions are applied to the same file, it becomes uncompilable. // https://github.com/rust-lang/rust/issues/112007 -fn bug_report<W: std::fmt::Write>(w: &mut W) -> std::fmt::Result { +pub fn bug_report<W: std::fmt::Write>(w: &mut W) -> std::fmt::Result { if true { writeln!(w, "`;?` here ->")?; } else { @@ -25,7 +33,7 @@ macro_rules! bar { } } -fn foo<W: std::fmt::Write>(w: &mut W) -> std::fmt::Result { +pub fn foo<W: std::fmt::Write>(w: &mut W) -> std::fmt::Result { if true { writeln!(w, "`;?` here ->")?; } else { @@ -34,4 +42,4 @@ fn foo<W: std::fmt::Write>(w: &mut W) -> std::fmt::Result { Ok(()) } -fn main() {} +pub fn main() {} diff --git a/tests/ui/typeck/issue-112007-leaked-writeln-macro-internals.stderr b/tests/ui/typeck/issue-112007-leaked-writeln-macro-internals.stderr index df2e06e8f3b..889d2c94d0c 100644 --- a/tests/ui/typeck/issue-112007-leaked-writeln-macro-internals.stderr +++ b/tests/ui/typeck/issue-112007-leaked-writeln-macro-internals.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-112007-leaked-writeln-macro-internals.rs:9:9 + --> $DIR/issue-112007-leaked-writeln-macro-internals.rs:17:9 | LL | / if true { LL | | writeln!(w, "`;?` here ->")?; @@ -21,9 +21,13 @@ help: you might have meant to return this value | LL | return writeln!(w, "but not here"); | ++++++ + +help: use the `?` operator to extract the `Result<(), std::fmt::Error>` value, propagating a `Result::Err` value to the caller + | +LL | writeln!(w, "but not here")? + | + error[E0308]: mismatched types - --> $DIR/issue-112007-leaked-writeln-macro-internals.rs:32:9 + --> $DIR/issue-112007-leaked-writeln-macro-internals.rs:40:9 | LL | / if true { LL | | writeln!(w, "`;?` here ->")?; @@ -44,6 +48,10 @@ help: you might have meant to return this value | LL | return baz!(w); | ++++++ + +help: use the `?` operator to extract the `Result<(), std::fmt::Error>` value, propagating a `Result::Err` value to the caller + | +LL | writeln!($w, "but not here")? + | + error: aborting due to 2 previous errors diff --git a/tests/ui/typeck/question-mark-operator-suggestion-span.rs b/tests/ui/typeck/question-mark-operator-suggestion-span.rs new file mode 100644 index 00000000000..7aea6e63dd1 --- /dev/null +++ b/tests/ui/typeck/question-mark-operator-suggestion-span.rs @@ -0,0 +1,22 @@ +// Check that we don't construct a span for `?` suggestions that point into non-local macros +// like into the stdlib where the user has no control over. +// +// FIXME(jieyouxu): this test is currently NOT run-rustfix because there are conflicting +// MaybeIncorrect suggestions: +// +// 1. adding `return ... ;`, and +// 2. adding `?`. +// +// When rustfix puts those together, the fixed file now contains uncompilable code. + +#![crate_type = "lib"] + +pub fn bug_report<W: std::fmt::Write>(w: &mut W) -> std::fmt::Result { + if true { + writeln!(w, "`;?` here ->")?; + } else { + writeln!(w, "but not here") + //~^ ERROR mismatched types + } + Ok(()) +} diff --git a/tests/ui/typeck/question-mark-operator-suggestion-span.stderr b/tests/ui/typeck/question-mark-operator-suggestion-span.stderr new file mode 100644 index 00000000000..089b3bcd198 --- /dev/null +++ b/tests/ui/typeck/question-mark-operator-suggestion-span.stderr @@ -0,0 +1,31 @@ +error[E0308]: mismatched types + --> $DIR/question-mark-operator-suggestion-span.rs:18:9 + | +LL | / if true { +LL | | writeln!(w, "`;?` here ->")?; +LL | | } else { +LL | | writeln!(w, "but not here") + | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found `Result<(), Error>` +LL | | +LL | | } + | |_____- expected this to be `()` + | + = note: expected unit type `()` + found enum `Result<(), std::fmt::Error>` + = note: this error originates in the macro `writeln` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider using a semicolon here + | +LL | }; + | + +help: you might have meant to return this value + | +LL | return writeln!(w, "but not here"); + | ++++++ + +help: use the `?` operator to extract the `Result<(), std::fmt::Error>` value, propagating a `Result::Err` value to the caller + | +LL | writeln!(w, "but not here")? + | + + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/typeck/return_type_containing_closure.rs b/tests/ui/typeck/return_type_containing_closure.rs index 8b826daeede..b81cac0a58a 100644 --- a/tests/ui/typeck/return_type_containing_closure.rs +++ b/tests/ui/typeck/return_type_containing_closure.rs @@ -1,5 +1,5 @@ #[allow(unused)] -fn foo() { //~ HELP a return type might be missing here +fn foo() { //~ HELP try adding a return type vec!['a'].iter().map(|c| c) //~^ ERROR mismatched types [E0308] //~| NOTE expected `()`, found `Map<Iter<'_, char>, ...>` diff --git a/tests/ui/typeck/return_type_containing_closure.stderr b/tests/ui/typeck/return_type_containing_closure.stderr index ea9c74be362..3f14650a82c 100644 --- a/tests/ui/typeck/return_type_containing_closure.stderr +++ b/tests/ui/typeck/return_type_containing_closure.stderr @@ -10,10 +10,10 @@ help: consider using a semicolon here | LL | vec!['a'].iter().map(|c| c); | + -help: a return type might be missing here +help: try adding a return type | -LL | fn foo() -> _ { - | ++++ +LL | fn foo() -> impl Iterator<Item = &char> { + | ++++++++++++++++++++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/ufcs/bad-builder.stderr b/tests/ui/ufcs/bad-builder.stderr index 9cfeb7a5d09..e466f94d0d8 100644 --- a/tests/ui/ufcs/bad-builder.stderr +++ b/tests/ui/ufcs/bad-builder.stderr @@ -9,7 +9,7 @@ note: if you're trying to build a new `Vec<Q>` consider using one of the followi Vec::<T>::with_capacity Vec::<T>::try_with_capacity Vec::<T>::from_raw_parts - and 4 others + and 6 others --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL help: there is an associated function `new` with a similar name | diff --git a/tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr b/tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr index bfa3061de08..cf4391311d0 100644 --- a/tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr +++ b/tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr @@ -7,6 +7,11 @@ LL | let f = to_fn(|| drop(x)); | -- ^ move occurs because `x` has type `Box<i32>`, which does not implement the `Copy` trait | | | captured by this `Fn` closure + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let f = to_fn(|| drop(x.clone())); + | ++++++++ error[E0507]: cannot move out of `x`, a captured variable in an `FnMut` closure --> $DIR/unboxed-closure-illegal-move.rs:19:35 @@ -17,6 +22,11 @@ LL | let f = to_fn_mut(|| drop(x)); | -- ^ move occurs because `x` has type `Box<i32>`, which does not implement the `Copy` trait | | | captured by this `FnMut` closure + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let f = to_fn_mut(|| drop(x.clone())); + | ++++++++ error[E0507]: cannot move out of `x`, a captured variable in an `Fn` closure --> $DIR/unboxed-closure-illegal-move.rs:28:36 diff --git a/tests/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-2.stderr b/tests/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-2.stderr index 5a76ef3e875..058dbb1e220 100644 --- a/tests/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-2.stderr +++ b/tests/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-2.stderr @@ -1,4 +1,4 @@ -error[E0282]: type annotations needed for `Option<T>` +error[E0282]: type annotations needed for `Option<_>` --> $DIR/unboxed-closures-failed-recursive-fn-2.rs:8:9 | LL | let mut closure0 = None; diff --git a/tests/ui/uninhabited/privately-uninhabited-mir-call.fixed b/tests/ui/uninhabited/privately-uninhabited-mir-call.fixed new file mode 100644 index 00000000000..76f4251daef --- /dev/null +++ b/tests/ui/uninhabited/privately-uninhabited-mir-call.fixed @@ -0,0 +1,31 @@ +// Verifies that MIR building for a call expression respects +// privacy when checking if a call return type is uninhabited. +//@ run-rustfix +#![allow(unreachable_code, unused_variables)] + +pub mod widget { + enum Unimplemented {} + pub struct Widget(Unimplemented); + + impl Widget { + pub fn new() -> Widget { + todo!(); + } + } + + pub fn f() { + let x: &mut u32; + Widget::new(); + // Ok. Widget type returned from new is known to be uninhabited + // and the following code is considered unreachable. + *x = 1; + } +} + +fn main() { + let y: &mut u32 = &mut 42; + widget::Widget::new(); + // Error. Widget type is not known to be uninhabited here, + // so the following code is considered reachable. + *y = 2; //~ ERROR E0381 +} diff --git a/tests/ui/uninhabited/privately-uninhabited-mir-call.rs b/tests/ui/uninhabited/privately-uninhabited-mir-call.rs index 2764bb563d3..1eec57ae046 100644 --- a/tests/ui/uninhabited/privately-uninhabited-mir-call.rs +++ b/tests/ui/uninhabited/privately-uninhabited-mir-call.rs @@ -1,5 +1,7 @@ // Verifies that MIR building for a call expression respects // privacy when checking if a call return type is uninhabited. +//@ run-rustfix +#![allow(unreachable_code, unused_variables)] pub mod widget { enum Unimplemented {} diff --git a/tests/ui/uninhabited/privately-uninhabited-mir-call.stderr b/tests/ui/uninhabited/privately-uninhabited-mir-call.stderr index 5f2f02c99fb..9d0771ad79e 100644 --- a/tests/ui/uninhabited/privately-uninhabited-mir-call.stderr +++ b/tests/ui/uninhabited/privately-uninhabited-mir-call.stderr @@ -1,5 +1,5 @@ error[E0381]: used binding `y` isn't initialized - --> $DIR/privately-uninhabited-mir-call.rs:28:5 + --> $DIR/privately-uninhabited-mir-call.rs:30:5 | LL | let y: &mut u32; | - binding declared here but left uninitialized @@ -9,7 +9,7 @@ LL | *y = 2; | help: consider assigning a value | -LL | let y: &mut u32 = todo!(); +LL | let y: &mut u32 = &mut 42; | +++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/union/union-borrow-move-parent-sibling.stderr b/tests/ui/union/union-borrow-move-parent-sibling.stderr index c9a440a66cc..782fa63280e 100644 --- a/tests/ui/union/union-borrow-move-parent-sibling.stderr +++ b/tests/ui/union/union-borrow-move-parent-sibling.stderr @@ -50,6 +50,11 @@ error[E0507]: cannot move out of dereference of `ManuallyDrop<((MockVec<u8>, Moc LL | let a = (u.x.0).0; | ^^^^^^^^^ move occurs because value has type `MockVec<u8>`, which does not implement the `Copy` trait | +note: if `MockVec<u8>` implemented `Clone`, you could clone the value + --> $DIR/union-borrow-move-parent-sibling.rs:25:1 + | +LL | struct MockVec<T> { + | ^^^^^^^^^^^^^^^^^ help: consider borrowing here | LL | let a = &(u.x.0).0; diff --git a/tests/ui/union/union-move.stderr b/tests/ui/union/union-move.stderr index 47fb801a50e..5ebb2716e5a 100644 --- a/tests/ui/union/union-move.stderr +++ b/tests/ui/union/union-move.stderr @@ -16,6 +16,11 @@ LL | fn move_out<T>(x: T) {} | -------- ^ this parameter takes ownership of the value | | | in this function +note: if `U1` implemented `Clone`, you could clone the value + --> $DIR/union-move.rs:9:1 + | +LL | union U1 { + | ^^^^^^^^ error[E0382]: use of moved value: `x` --> $DIR/union-move.rs:42:18 @@ -35,6 +40,11 @@ LL | fn move_out<T>(x: T) {} | -------- ^ this parameter takes ownership of the value | | | in this function +note: if `U1` implemented `Clone`, you could clone the value + --> $DIR/union-move.rs:9:1 + | +LL | union U1 { + | ^^^^^^^^ error[E0509]: cannot move out of type `U2`, which implements the `Drop` trait --> $DIR/union-move.rs:49:18 @@ -44,6 +54,11 @@ LL | move_out(x.f1_nocopy); | | | cannot move out of here | move occurs because `x.f1_nocopy` has type `ManuallyDrop<RefCell<i32>>`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL | move_out(x.f1_nocopy.clone()); + | ++++++++ error: aborting due to 3 previous errors diff --git a/tests/ui/variance/variance-issue-20533.rs b/tests/ui/variance/variance-issue-20533.rs index a2459f8730c..4c109608906 100644 --- a/tests/ui/variance/variance-issue-20533.rs +++ b/tests/ui/variance/variance-issue-20533.rs @@ -19,8 +19,15 @@ fn baz<'a, T>(_x: &'a T) -> Baked<'a> { Baked(PhantomData) } +fn bat(x: &AffineU32) -> &u32 { + &x.0 +} + struct AffineU32(u32); +#[derive(Clone)] +struct ClonableAffineU32(u32); + fn main() { { let a = AffineU32(1); @@ -40,4 +47,16 @@ fn main() { drop(a); //~ ERROR cannot move out of `a` drop(x); } + { + let a = AffineU32(1); + let x = bat(&a); + drop(a); //~ ERROR cannot move out of `a` + drop(x); + } + { + let a = ClonableAffineU32(1); + let x = foo(&a); + drop(a); //~ ERROR cannot move out of `a` + drop(x); + } } diff --git a/tests/ui/variance/variance-issue-20533.stderr b/tests/ui/variance/variance-issue-20533.stderr index 258f67db5ce..4515d313ec0 100644 --- a/tests/ui/variance/variance-issue-20533.stderr +++ b/tests/ui/variance/variance-issue-20533.stderr @@ -1,5 +1,5 @@ error[E0505]: cannot move out of `a` because it is borrowed - --> $DIR/variance-issue-20533.rs:28:14 + --> $DIR/variance-issue-20533.rs:35:14 | LL | let a = AffineU32(1); | - binding `a` declared here @@ -9,9 +9,15 @@ LL | drop(a); | ^ move out of `a` occurs here LL | drop(x); | - borrow later used here + | +note: if `AffineU32` implemented `Clone`, you could clone the value + --> $DIR/variance-issue-20533.rs:26:1 + | +LL | struct AffineU32(u32); + | ^^^^^^^^^^^^^^^^ error[E0505]: cannot move out of `a` because it is borrowed - --> $DIR/variance-issue-20533.rs:34:14 + --> $DIR/variance-issue-20533.rs:41:14 | LL | let a = AffineU32(1); | - binding `a` declared here @@ -21,9 +27,15 @@ LL | drop(a); | ^ move out of `a` occurs here LL | drop(x); | - borrow later used here + | +note: if `AffineU32` implemented `Clone`, you could clone the value + --> $DIR/variance-issue-20533.rs:26:1 + | +LL | struct AffineU32(u32); + | ^^^^^^^^^^^^^^^^ error[E0505]: cannot move out of `a` because it is borrowed - --> $DIR/variance-issue-20533.rs:40:14 + --> $DIR/variance-issue-20533.rs:47:14 | LL | let a = AffineU32(1); | - binding `a` declared here @@ -33,7 +45,48 @@ LL | drop(a); | ^ move out of `a` occurs here LL | drop(x); | - borrow later used here + | +note: if `AffineU32` implemented `Clone`, you could clone the value + --> $DIR/variance-issue-20533.rs:26:1 + | +LL | struct AffineU32(u32); + | ^^^^^^^^^^^^^^^^ + +error[E0505]: cannot move out of `a` because it is borrowed + --> $DIR/variance-issue-20533.rs:53:14 + | +LL | let a = AffineU32(1); + | - binding `a` declared here +LL | let x = bat(&a); + | -- borrow of `a` occurs here +LL | drop(a); + | ^ move out of `a` occurs here +LL | drop(x); + | - borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let x = bat(&a).clone(); + | ++++++++ + +error[E0505]: cannot move out of `a` because it is borrowed + --> $DIR/variance-issue-20533.rs:59:14 + | +LL | let a = ClonableAffineU32(1); + | - binding `a` declared here +LL | let x = foo(&a); + | -- borrow of `a` occurs here +LL | drop(a); + | ^ move out of `a` occurs here +LL | drop(x); + | - borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL - let x = foo(&a); +LL + let x = foo(a.clone()); + | -error: aborting due to 3 previous errors +error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0505`. |
